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.
To get started, we need to add react-fela
to our project:
$ npm install --save react-fela
$ 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.
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.
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.
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
.
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!
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.
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.
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!