React component styling solved with an elegant (inspired) API,
small footprint (<5kb gzipped), and great performance (via glamor
).
Read the intro blogpost
You like CSS in JS, but you don't like having to create entire component
functions just for styling purposes. You don't want to give a name to something
that's purely style-related. And it's just kind of annoying to do the
style-creating, className
assigning, and props-forwarding song and dance.
For example, this is what you have to do with raw glamor
(or aphrodite
or
similar for that matter):
const styles = glamor.css({
fontSize: 20,
textAlign: 'center',
})
function MyStyledDiv({className = '', ...rest}) {
return (
<div
className={`${styles} ${className}`}
{...rest}
/>
)
}
With glamorous
, that example above looks as simple as this:
const MyStyledDiv = glamorous.div({
fontSize: 20,
textAlign: 'center',
})
In fact, it's even better, because there are a bunch of features that make composing these components together really nice!
Oh, and what if you didn't care to give MyStyledDiv
a name? If you just want
a div that's styled using glamor? You can do that too:
const { Div } = glamorous
function App() {
return (
<Div
fontSize={20}
textAlign="center"
>
Hello world!
</Div>
)
}
So that's the basics of this solution... Let's get to the details!
This module is distributed via npm which is bundled with node and
should be installed as one of your project's dependencies
:
npm install --save glamorous
This also depends on react
and glamor
so you'll need those in your project
as well (if you don't already have them):
npm install --save react glamor
You can then use one of the module formats:
main
:dist/glamorous.cjs.js
- exports itself as a CommonJS moduleglobal
:dist/glamorous.umd.js
anddist/glamorous.umd.min.js
- exports itself as a umd module which is consumable in several environments, the most notable as a global.jsnext:main
andmodule
:dist/glamorous.es.js
- exports itself using the ES modules specification, you'll need to configure webpack to make use of this file do this using the resolve.mainFields property.
The most common use-case is consuming this module via CommonJS:
const glamorous = require('glamorous')
// etc.
If you're transpiling (and/or using the jsnext:main
):
import glamorous from 'glamorous'
If you want to use the global:
<!-- Load dependencies -->
<script src="https://unpkg.com/react/dist/react.js"></script>
<script src="https://unpkg.com/glamor/umd/index.js"></script>
<!-- load library -->
<script src="https://unpkg.com/glamorous/dist/glamorous.umd.js"></script>
<script>
// Use window.glamorous here...
</script>
The glamorous
function is the main (only) export. It allows you to create
glamorous components that render the styles to the component you give it. This
is done by forwarding a className
prop to the component you tell it to render.
But before we get into how you wrap custom components, let's talk about the
built-in DOM components.
For every DOM element, there is an associated glamorous
component factory
attached to the glamorous
function. As above, you can access these factories
like so: glamorous.div
, glamorous.a
, glamorous.article
, etc.
Whether you create one yourself or use one of the built-in ones mentioned above,
each glamorousComponentFactory
allows you to invoke it with styles and it
returns you a new component which will have those styles applied when it's
rendered. This is accomplished by generating a className
for the styles you
give and forwarding that className
onto the rendered element. So if you're
wrapping a component you intend to style, you'll need to make sure you accept
the className
as a prop and apply it to where you want the styles applied in
your custom component (normally the root element).
The glamorousComponentFactory
accepts any number of style object arguments.
These can be style objects or functions which are invoked with props
on every
render and return style objects. To learn more about what these style objects
can look like, please take a look at the glamor
documentation.
The GlamorousComponent
is what is returned from the
glamorousComponentFactory
. Its job is to get all the styles together get a
className
(from glamor
) and forward that on to your component.
For examples below, we'll use this as our GlamorousComponent:
const MyStyledDiv = glamorous.div({margin: 1, fontSize: 1, padding: 1})
It does a few interesting things based on the props you pass it:
For each className
you provide, the GlamorousComponent
will check to see
whether it is a glamor
generated className
(can be from raw glamor
or from glamorous
, doesn't matter). If it is, it will get the original styles
that were used to generate that className
and merge those with the styles for
the element that's rendered in a way that the provided className
's styles win
in the event of a conflict.
If the className
is not generated by glamor
, then it will simply be
forwarded along with the GlamorousComponent
-generated className
.
const myCustomGlamorStyles = glamor.css({fontSize: 2})
<MyStyledDiv className={`${myCustomGlamorStyles} custom-class`} />
// styles applied:
// {margin: 1, fontSize: 2, padding: 1}
// as well as any styles custom-class applies
This is an object and if provided, it will be merged with this component's and take highest priority over the component's predefined styles.
const myCustomGlamorStyles = glamor.css({fontSize: 2, padding: 2})
<MyStyledDiv
className={`${myCustomGlamorStyles} custom-class`}
cssOverrides={{padding: 3}}
/>
// styles applied:
// {margin: 1, fontSize: 2, padding: 3}
// as well as any styles custom-class applies
Only props that are safe to forward to the specific element
that will
ultimately be rendered will be forwarded. So this is totally legit:
<MyStyledDiv size="big" />
A use case for doing something like this would be for dynamic styles:
const staticStyles = {color: 'green'}
const dynamicStyles = props => {fontSize: props.size === 'big' ? 32 : 24}
const MyDynamicallyStyledDiv = glamorous.div(staticStyles, dynamicStyles)
The exception to this prop forwarding is the pre-created
GlamorousComponent
s (see below).
Often you want to style something without actually giving it a name (because
naming things is hard). So glamorous also exposes a pre-created
GlamorousComponent
for each DOM node type which make this reasonable to do:
const { Div, Span, A, Img } = glamorous
function MyUserInterface({name, tagline, imageUrl, homepage, size}) {
const nameSize = size
const taglineSize = size * 0.5
return (
<Div display="flex" flexDirection="column" justifyContent="center">
<A href={homepage} textDecoration="underline" color="#336479">
<Img borderRadius="50%" height={180} src={imageUrl} />
<Div fontSize={nameSize} fontWeight="bold">{name}</Div>
</A>
<Span fontSize={taglineSize} color="#767676">
{tagline}
</Span>
</Div>
)
}
Having to name all of that stuff could be tedious, so having these pre-built
components is handy. The other handy bit here is that the props are the styles
for these components. Notice that glamorous can distinguish between props that
are for styling and those that are have semantic meaning (like with the Img
and A
components which make use of src
and href
props).
One other tip... This totally works:
<glamorous.Div color="blue">
JSX is pretty wild!
</glamorous.Div>
Because both glamor
and react
support SSR, glamorous
does too! I actually
do this on my personal site
which is generated at build-time on the server. Learn about rendering
react
on the server and glamor
too.
This package was inspired by the work from people's work on the following projects:
The biggest inspiration for building this is because I love the API offered by
styled-components
, but I wanted:
- Not to ship a CSS parser to the browser (because it's huge and less performant).
- Support for RTL (via something like rtl-css-js)
- Support for using real JavaScript objects rather than a CSS string (better tooling support, ESLint, etc.)
You can get around the parser issue if you use a certain babel plugin, but then you can't do any dynamic construction of your CSS string (like this) which is a bummer for custom utilities.
There are actually quite a few solutions to the general problem of styling in React. This isn't the place for a full-on comparison of features, but I'm unaware of any which supports all of the features which this library supports.
If you need help, please fork this codepen and bring it up in the chat
Thanks goes to these people (emoji key):
Kent C. Dodds 💻 📖 🚇 |
Ives van Hoorne 💡 |
Gerardo Nardelli 📖 |
Chandan Rai 📖 |
---|
This project follows the all-contributors specification. Contributions of any kind welcome!
MIT