Tutorial

Styling Components with React Fela

Updated on April 27, 2020
author

joshtronic

Styling Components with React Fela

Styling in React comes in all shapes and sizes. Out of the box, you can assign classes via the className property or assign CSS properties by passing an object to the style property. While sufficient enough in most scenarios, they are not without their shortcomings. Making classNames dynamic requires defining additional classes, and styles, while allowing dynamic properties, still requires boilerplate code and doesn’t allow you dig deep into pseudo-class properties like :hover. That’s where Fela can come to your rescue!

Fela is a framework-agnostic library for handling state-driven styling in JavaScript. It’s highly performant and gives you some flexibility in regard to how you use it. While you can use Fela by itself, react-fela is available to provide React bindings for your React.js apps.

Getting Started

To get started, we need to add react-fela to our project:

Via npm

$ npm install --save react-fela

Or via Yarn

$ yarn add react-fela

For this article we will be using some methods from the fela library directly. Don’t worry though, react-fela includes this dependency so we should be good to go.

The Renderer

As promised, there is a part of the fela library that we will need to use, that’s createRenderer. The createRenderer method is used to create a renderer that will be passed a Provider component that will wrap out components that are leveraging Fela.

All of the following examples will include the boilerplate code that we need to make the magic happen.

Using Generated CSS Classes

The least complex Fela example isn’t ideal but does have the least amount of boilerplate code.

The way Fela works is that it takes your styles, generates the appropriate CSS with atomic classes and allows you to grab the CSS classes that can be passed to any component’s classNames property:

import React from "react";
import { render } from "react-dom";

import { createRenderer } from "fela";
import { Provider } from "react-fela";

const renderer = createRenderer();

const rule = ({
  backgroundColor = "#6db65b",
  borderColor = "#efbb35",
  padding
}) => ({
  backgroundColor: backgroundColor,
  border: `10px solid ${borderColor}`,
  color: "#fff",
  fontWeight: "bold",
  padding: `${padding}px`,
  ":hover": {
    cursor: "pointer",
    filter: "drop-shadow(0 10px 19px rgba(0, 0, 0, 0.3))"
  }
});

const container = document.createElement("div");
document.body.appendChild(container);
render(
  <Provider renderer={renderer}>
    <div className={renderer.renderRule(rule, { padding: 100 })}>
      Hover Over Me!
    </div>
  </Provider>,
  container
);

As you can see in the example, the renderer is used to generate our CSS and class names based on the object of CSS properties being passed-in.

The rule is just a simple anonymous function that returns an object with all of our properties including any properties that may have been passed-in.

Because rule is just a function with a return object, you could expand and include additional logic as you see fit instead of immediately returning the expected object.

Using Component Primitives

While the previous approach works well enough, sometimes it’s nice to create a new component that could easily be reused throughout your project.

In those scenarios, we can leverage the FelaComponent primitive component to create a new component:

import React from "react";
import { render } from "react-dom";

import { createRenderer } from "fela";
import { FelaComponent, Provider } from "react-fela";

const renderer = createRenderer();

const rule = ({
  backgroundColor = "#6db65b",
  borderColor = "#efbb35",
  padding
}) => ({
  backgroundColor: backgroundColor,
  border: `10px solid ${borderColor}`,
  color: "#fff",
  fontWeight: "bold",
  padding: `${padding}px`,
  ":hover": {
    cursor: "pointer",
    filter: "drop-shadow(0 10px 19px rgba(0, 0, 0, 0.3))"
  }
});

const PaddedContainer = ({
  backgroundColor,
  borderColor,
  padding,
  children
}) => (
  <FelaComponent
    rule={rule}
    backgroundColor={backgroundColor}
    borderColor={borderColor}
    padding={padding}
  >
    {children}
  </FelaComponent>
);

const container = document.createElement("div");
document.body.appendChild(container);
render(
  <Provider renderer={renderer}>
    <PaddedContainer padding={100}>
      Hover Over Me!
    </PaddedContainer>
  </Provider>,
  container
);

Utilizing a similar rule, we are able to create a new PaddedContainer component that we can pass properties to directly without the overhead and assigning classes via renderRule.

Creating Components Yourself

There’s nothing wrong with using primitives to create new components, but you tend to wind up with a bit more boilerplate than you would by creating components directly:

import React from "react";
import { render } from "react-dom";

import { createRenderer } from "fela";
import { createComponent, Provider } from "react-fela";

const renderer = createRenderer();

const rule = ({
  backgroundColor,
  borderColor,
  padding
}) => ({
  backgroundColor: backgroundColor,
  border: `10px solid ${borderColor}`,
  color: "#fff",
  fontWeight: "bold",
  padding: `${padding}px`,
  ":hover": {
    cursor: "pointer",
    filter: "drop-shadow(0 10px 19px rgba(0, 0, 0, 0.3))"
  }
});

const AnotherPaddedContainer = createComponent(rule);

const container = document.createElement("div");
document.body.appendChild(container);
render(
  <Provider renderer={renderer}>
    <AnotherPaddedContainer padding={100}>Hover Over Me!</AnotherPaddedContainer>
  </Provider>,
  container
);

A bit cleaner and just as reusable as our example with primitive components!

Conclusion

Regardless of which approach you take, leveraging react-fela can speed up the time necessary to dynamically style components in React by eliminating the amount of boilerplate you have to write.

To see the code samples above in action, head over to CodeSandbox.

Enjoy!

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
joshtronic

author

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.