Glamorous is my favorite CSS-in-JS library for React and React Native. Coupled with styled-system, polished and prop-styles, it brings you the most complete and flexible approach to component styling.
Among the many reasons to prefer Glamorous, is the ability to use JSX props for styling and not having to...
Let's take a look at an example of what I mean:
<Box flex={1} justify="center" align="center">
{/*...*/}
</Box>
See? I didn't have to name this particular instance of Box
, and there's
only a couple of style overrides I was interested in, I didn't have to create
a new component for it.
Glamorous (technically Glamor, which is based upon) allows many ways to override a component's style in addition to the option above. However, these seem to only work on components created by its component factory, not with normal DOM elements. You can see the difference here:
import React from 'react';
import glamorous from 'glamorous';
const Elements = () => (
<div>
<glamorous.H1 css={{ color: 'blue' }}>Hi, I'm blue</glamorous.H1>
<h1 css={{ color: 'blue' }}>I am NOT (yet)</h1>
</div>
);
The second example would not work, as this is a "default" element. However, there's a way to make that work if you're interested in having consistency across your project above all.
I've chosen a NextJS app for this example, so we'll look at adding Glamorous and enabling the native element style in order.
In order to add Glamorous to a NextJS (including being compatible with SSR), we need to add them to our project:
$ yarn add glamorous glamor
Then we need to add the following to pages/_document.js
(straight from
their example page):
/* eslint-disable react/no-danger */
import React from 'react';
import Document, { Head, Main, NextScript } from 'next/document';
import { renderStatic } from 'glamor/server';
export default class MyDocument extends Document {
static async getInitialProps({ renderPage }) {
const page = renderPage();
const styles = renderStatic(() => page.html || page.errorHtml);
return { ...page, ...styles };
}
constructor(props) {
super(props);
const { __NEXT_DATA__, ids } = props;
if (ids) {
__NEXT_DATA__.ids = this.props.ids;
}
}
render() {
return (
<html lang="en">
<Head>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<style dangerouslySetInnerHTML={{ __html: this.props.css }} />
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
With this in place, we're able to use Glamorous as we would in any other app, so let's enable native DOM element styling now.
The reason we can't style native elements (lowercase elements, e.g h1
, div
, etc),
is that the custom props are only enabled for components created by Glamorous
component factories, which in turn create Glamor elements.
But as you might remember, JSX is mostly syntactic sugar to React.createElement
,
and in our example, our <h1>{/*...*/}</h1>
would get translated to
React.createElement('h1', /*...*/)
. We can take advantage of that, and
just replace the calls.
In our .babelrc
, we make a couple of changes, instructing Babel to use
Glamor.createElement
instead of React.createElement
:
{
"presets": ["next/babel", "@babel/stage-3", "@babel/flow"],
"plugins": [
["@babel/transform-react-jsx", { "pragma": "Glamor.createElement"}],
"glamor/babel-hoist"
]
}
Please notice this is Babel 7, but the process should be very similar for version 6.
We took care of replacing the calls, but Glamor
would still need to be in scope,
in the same way React
should. Otherwise we would to add the following
statement to every file:
import Glamor from 'glamor';
But we remember that, in NextJS, importing React in your pages/components
is not actually required, and we can achieve something similar for Glamor
.
How so? We modify our Webpack config in our next.config.js
:
const webpack = require('webpack');
module.exports = {
webpack: config => {
config.plugins.push(new webpack.ProvidePlugin({ Glamor: `glamor/react` }));
return config;
},
};
And that is all we need! Now we can style native elements, like so:
export default () => <h1 css={{ color: "blue" }}>Hello World!</h1>