Advanced Guides

This is some of the more advanced stuff you can do with glamorous

innerRef

How to access the underlying ref

Sometimes you need access to the ref of the underlying component that's rendered. You can accomplish this with the innerRef prop.

This is a function and if provided, will be called with the inner element's reference.

An input in a form

Working with existing CSS

Often you'll bring glamorous into an existing project which is already using global CSS. Many of the glamorous APIs make working with this as easy as possible.

glamorous works out of the box with CSS modules too. It Just Worksβ„’.

Here's an example using Bootstrap:

// source https://v4-alpha.getbootstrap.com/components/alerts/

import React from 'react';
import { render } from 'react-dom';
import glamorous from 'glamorous';

const Alert = glamorous.div('alert', props => `alert-${props.type}`);

function App() {
  return (
    <glamorous.Div maxWidth={600} margin="70px auto" fontSize={24}>
      <Alert type="success">
        <strong>Success!</strong> Tada! πŸŽ‰
      </Alert>
      <Alert type="info">
        <strong>Heads up!</strong> Some info here ℹ️
      </Alert>
      <Alert type="warning">
        <strong>Warning!</strong> Something's up ⚠️
      </Alert>

      <Alert type="danger">
        <strong>Oh snap!</strong> This is not good 🚨
      </Alert>
    </glamorous.Div>
  );
}

render(<App />, document.getElementById('root'));

Try this out in your browser here!

If suits your fancy better than that's simple:

// source https://v4-alpha.getbootstrap.com/components/alerts/

import React from 'react';
import { render } from 'react-dom';
import glamorous from 'glamorous';

const Alert = glamorous.div('alert', props => {
  const types = ['success', 'info', 'warning', 'danger'];
  return types.reduce((all, t) => {
    if (props.hasOwnProperty(t)) {
      all = `${all} alert-${t}`;
    }
    return all;
  }, '');
});

function App() {
  return (
    <glamorous.Div maxWidth={600} margin="70px auto" fontSize={24}>
      <Alert success>
        <strong>Success!</strong> Tada! πŸŽ‰
      </Alert>
      <Alert info>
        <strong>Heads up!</strong> Some info here ℹ️
      </Alert>
      <Alert warning>
        <strong>Warning!</strong> Something's up ⚠️
      </Alert>
      <Alert danger>
        <strong>Oh snap!</strong> This is not good 🚨
      </Alert>
    </glamorous.Div>
  );
}

render(<App />, document.getElementById('root'));

Try this out in your browser here!

Remember this

With CSS in JS, the goal is to style components and reuse those components. With this in mind, if you need to style your entire application (like html/body or add some reset styles), you wont do this with glamorous. Instead you can use regular CSS or use glamor's API for injecting global styles.

In addition, rather than using CSS to style an a tag with global CSS, you should create a Link component with all the styles you need and reuse that.

Theming

glamorous fully supports theming using a special <ThemeProvider> component.

It provides the theme to all glamorous components down the tree.

import glamorous, {ThemeProvider} from 'glamorous'

// our main theme object
const theme = {
  main: {color: 'red'}
}

// our secondary theme object
const secondaryTheme = {
  main: {color: 'blue'}
}

// a themed <Title> component
const Title = glamorous.h1({
  fontSize: '10px'
}, ({theme}) => ({
  color: theme.main.color
}))

// use <ThemeProvider> to pass theme down the tree
<ThemeProvider theme={theme}>
  <Title>Hello!</Title>
</ThemeProvider>

// it is possible to nest themes
// inner themes will be merged with outers
<ThemeProvider theme={theme}>
  <div>
    <Title>Hello!</Title>
    <ThemeProvider theme={secondaryTheme}>
      {/* this will be blue */}
      <Title>Hello from here!</Title>
    </ThemeProvider>
  </div>
</ThemeProvider>

// to override a theme, just pass a theme prop to a glamorous component
// the component will ignore any surrounding theme, applying the one passed directly via props
<ThemeProvider theme={theme}>
  {/* this will be yellow */}
  <Title theme={{main: {color: 'yellow'}}}>Hello!</Title>
</ThemeProvider>

Try this out in your browser here!

glamorous also exports a withTheme higher order component (HOC) so you can access your theme in any component!

import glamorous, {ThemeProvider,  withTheme} from 'glamorous'

// our main theme object
const theme = {
  main: {color: 'red'}
}

// a themed <Title> component
const Title = glamorous.h1({
  fontSize: '10px'
}, ({theme}) => ({
  color: theme.main.color
}))

// normal component that takes a theme prop
const SubTitle = ({children, theme: {color}}) => (
  <h3 style={{color}}>{children}</h3>
)

// extended component with theme prop
const ThemedSubTitle = withTheme(SubTitle)

<ThemeProvider theme={theme}>
  <Title>Hello!</Title>
  <ThemedSubTitle>from withTheme!</ThemedSubTitle>
</ThemeProvider>

Try this out in your browser here!

Or if you prefer decorator syntax:

import React, {Component} from 'react'
import glamorous, {ThemeProvider,  withTheme} from 'glamorous'

// our main theme object
const theme = {
  main: {color: 'red'}
}

// a themed <Title> component
const Title = glamorous.h1({
  fontSize: '10px'
}, ({theme}) => ({
  color: theme.main.color
}))

// extended component with theme prop
@withTheme
class SubTitle extends Component {
  render() {
    const {children, theme: {main: {color}}} = this.props
    return <h3 style={{color}}>{children}</h3>
  }
}

<ThemeProvider theme={theme}>
  <Title>Hello!</Title>
  <SubTitle>from withTheme!</SubTitle>
</ThemeProvider>

withTheme expects a ThemeProvider further up the render tree and will warn in development if one is not found!

CodeSandbox

Context

context is an unstable API and it's not recommended to use it directly. However, if you need to use it for some reason, here's an example of how you could do that:

const dynamicStyles = (props, context) => ({
  color: context.isLoggedIn ? 'green' : 'red'
})
const MyDiv = glamorous.div(dynamicStyles)
MyDiv.contextTypes = {
  isLoggedIn: PropTypes.string,
}

class Parent extends React.Component {
  getChildContext() {
    return {
      isLoggedIn: true,
    }
  }
  render() {
    return <MyDiv />
  }
}

Parent.childContextTypes = {
  isLoggedIn: PropTypes.string,
}

<Parent />
// renders <div />
// with {color: 'green'}

Optimizing Bundle Size

If your use case is really size constrained, then you might consider using the "tiny" version of glamorous for your application. It is a miniature version of glamorous with a few limitations:

  1. No built-in component factories (glamorous.article({/* styles */ })) So you have to create your own (glamorous('article')({/* styles */ }))
  2. No built-in glamorous components (glamorous.Span)
  3. No props filtering for dynamic styles, instead, you place them on a special glam prop (see the example below).
  4. If you need ThemeProvider or withTheme, you must import those manually. They are not exported as part of glamorous/ tiny like they are with glamorous.

Here's an example of what you're able to do with it.

import React from 'react'
import glamorous from 'glamorous/dist/glamorous.es.tiny'

const Comp = glamorous('div')({
  color: 'red'
}, (props) => ({
  fontSize: props.glam.big ? 20 : 12
}))
function Root() {
  return (
    <Comp
      glam={{ big: true }}
      thisWillBeForwardedAndReactWillWarn
    >
      ciao
    </Comp>
  )
}

export default Root

Improved Experience

It's recommended to use either babel-plugin-module-resolver or the resolve.alias config with webpack so you don't have to import from that full path.

You have the following options available for this import:

  1. glamorous/dist/glamorous.es.tiny.js - use if you're using Webpack@>=2 or Rollup
  2. glamorous/dist/glamorous.cjs.tiny.js - use if you're not transpiling ESModules
  3. glamorous/dist/glamorous.umd.tiny.js - use if you're including it as a script tag. (There's also a .min.js version).

The current size of glamorous/dist/glamorous.umd.min.tiny.js is: tiny size tiny gzip size

Important note

Because glamorous depends on glamor, you should consider the full size you'll be adding to your application if you don't already have glamor. The current size of glamor/umd/index.min.js is: glamor size glamor gzip size

Server Side Rendering

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.

To perform server-side rendering, use renderStatic from glamor, which takes a callback. Render your component inside the callback and all of the calls to css() will be collected and generated html and css will be returned. This will also return an array of ids to rehydrate the styles for fast startup.

To perform rehydration, call rehydrate with the array of ids returned by renderStatic.

Example -

import {renderStatic} from 'glamor/server'
import {rehydrate} from 'glamor'
import {render} from 'react-dom'
import ReactDOMServer from 'react-dom/server'

let {html, css, ids} = renderStatic(() => ReactDOMServer.renderToString(<App />))

return `
  <html>
    <head>
      <style>${css}</style>
    </head>
    <body>
      <div id="app">${html}</div>
      <script src="./bundle.js"></script>
      <script>
        rehydrate(${JSON.stringify(ids)});
        render(<App />, document.getElementById('app'));
      </script>
    </body>
  </html>
`

Usage with Next.js

Check out this example on how to use glamorous with Next.js

Using rehydrate

Glamorous might add duplicate styles to the page due to the intricate nature of es6 imports.

For example:

import App from './App';

rehydrate(${JSON.stringify(ids)}) // ids returned by renderStatic

As import statements get transpiled before other statements, so rehydrate will be called after regardless of what order you list the statements. This will result in duplicate styles because App is imported first, so all the styles are added before rehydrate runs.

To tackle this problem, use a require() call to order the statements.

rehydrate(${JSON.stringify(ids)});

const App = require('./App');

Or rehydrate must be run before any other style code if you use any other solution.

Contributors:

kentcdodds's GitHub avatarpaulmolluzzo's GitHub avatarJReinhold's GitHub avatar