Skip to main content

CSS-in-JavaScript

The Airbnb CSS-in-JavaScript style guide covers how to write style objects consistently — from naming conventions and ordering to theming and fallback strategies.

Naming

Use camelCase for object keys (selectors).
You access these keys as properties on the styles object in the component, so camelCase is the most convenient choice.
{
  bermudaTriangle: {
    display: 'none',
  },
}
Use an underscore for modifiers to other styles.
Similar to BEM, this naming convention makes it clear that the styles are intended to modify the element preceded by the underscore. Underscores do not need to be quoted, so they are preferred over dashes.
// bad
{
  bruceBanner: {
    color: 'pink',
    transition: 'color 10s',
  },

  bruceBannerTheHulk: {
    color: 'green',
  },
}

// good
{
  bruceBanner: {
    color: 'pink',
    transition: 'color 10s',
  },

  bruceBanner_theHulk: {
    color: 'green',
  },
}
Use selectorName_fallback for sets of fallback styles.
Keeping the naming consistent reveals the relationship between fallback styles and the styles that override them in more capable browsers.
// bad
{
  muscles: {
    display: 'flex',
  },

  muscles_sadBears: {
    width: '100%',
  },
}

// good
{
  muscles: {
    display: 'flex',
  },

  muscles_fallback: {
    width: '100%',
  },
}
Use a separate selector for sets of fallback styles.
Keeping fallback styles contained in a separate object clarifies their purpose and improves readability.
// bad
{
  muscles: {
    display: 'flex',
  },

  left: {
    flexGrow: 1,
    display: 'inline-block',
  },

  right: {
    display: 'inline-block',
  },
}

// good
{
  muscles: {
    display: 'flex',
  },

  left: {
    flexGrow: 1,
  },

  left_fallback: {
    display: 'inline-block',
  },

  right_fallback: {
    display: 'inline-block',
  },
}
Use device-agnostic names (for example, small, medium, and large) to name media query breakpoints.
Commonly used names like “phone”, “tablet”, and “desktop” do not match the characteristics of real-world devices and set the wrong expectations.
// bad
const breakpoints = {
  mobile: '@media (max-width: 639px)',
  tablet: '@media (max-width: 1047px)',
  desktop: '@media (min-width: 1048px)',
};

// good
const breakpoints = {
  small: '@media (max-width: 639px)',
  medium: '@media (max-width: 1047px)',
  large: '@media (min-width: 1048px)',
};

Ordering

Define styles after the component.
A higher-order component is naturally used after the component definition. Passing the styles object directly to this function reduces indirection.
function MyComponent({ styles }) {
  return (
    <div {...css(styles.container)}>
      Never doubt that a small group of thoughtful, committed citizens can
      change the world. Indeed, it's the only thing that ever has.
    </div>
  );
}

export default withStyles(() => ({
  container: {
    display: 'inline-block',
  },
}))(MyComponent);

Nesting

Leave a blank line between adjacent blocks at the same indentation level.
The whitespace improves readability and reduces the likelihood of merge conflicts.
{
  bigBang: {
    display: 'inline-block',

    '::before': {
      content: "''",
    },
  },

  universe: {
    border: 'none',
  },
}

Inline styles

Use inline styles for styles that have high cardinality (for example, a value derived from a prop), and not for styles with low cardinality.
Generating themed stylesheets can be expensive, so they are best for discrete sets of styles.
function MyComponent({ styles, spacing }) {
  return (
    <div {...css(styles.periodic, { margin: spacing })} />
  );
}
export default withStyles(() => ({
  periodic: {
    display: 'table',
  },
}))(MyComponent);
Use an inline style only for the part of the style that changes with every render (like margin: spacing). Put static properties in a stylesheet style object.

Themes

Use an abstraction layer such as react-with-styles that enables theming.
A shared set of style variables makes your components more consistent and easier to maintain. An abstraction layer also decouples your components from any particular underlying implementation.
Define colors only in themes.
// bad
export default withStyles(() => ({
  chuckNorris: {
    color: '#bada55',
  },
}))(MyComponent);

// good
export default withStyles(({ color }) => ({
  chuckNorris: {
    color: color.badass,
  },
}))(MyComponent);
Define fonts only in themes.
// bad
export default withStyles(() => ({
  towerOfPisa: {
    fontStyle: 'italic',
  },
}))(MyComponent);

// good
export default withStyles(({ font }) => ({
  towerOfPisa: {
    fontStyle: font.italic,
  },
}))(MyComponent);
Define fonts as sets of related styles.
// bad
export default withStyles(() => ({
  towerOfPisa: {
    fontFamily: 'Italiana, "Times New Roman", serif',
    fontSize: '2em',
    fontStyle: 'italic',
    lineHeight: 1.5,
  },
}))(MyComponent);

// good
export default withStyles(({ font }) => ({
  towerOfPisa: {
    ...font.italian,
  },
}))(MyComponent);
Define base grid units in the theme, either as a value or a function that takes a multiplier.
// bad
export default withStyles(() => ({
  rip: {
    bottom: '-6912px', // 6 feet
  },
}))(MyComponent);

// good
export default withStyles(({ units }) => ({
  rip: {
    bottom: units(864), // 6 feet, assuming our unit is 8px
  },
}))(MyComponent);

// good
export default withStyles(({ unit }) => ({
  rip: {
    bottom: 864 * unit, // 6 feet, assuming our unit is 8px
  },
}))(MyComponent);
Define media queries only in themes.
// bad
export default withStyles(() => ({
  container: {
    width: '100%',

    '@media (max-width: 1047px)': {
      width: '50%',
    },
  },
}))(MyComponent);

// good
export default withStyles(({ breakpoint }) => ({
  container: {
    width: '100%',

    [breakpoint.medium]: {
      width: '50%',
    },
  },
}))(MyComponent);
Define tricky fallback properties in themes.
Many CSS-in-JavaScript implementations merge style objects together, which makes specifying fallbacks for the same property (for example, display) tricky. Putting these fallbacks in the theme keeps the approach unified.
// bad
export default withStyles(() => ({
  muscles: {
    display: 'flex',
  },

  muscles_fallback: {
    'display ': 'table',
  },
}))(MyComponent);

// good
export default withStyles(({ fallbacks }) => ({
  muscles: {
    display: 'flex',
  },

  muscles_fallback: {
    [fallbacks.display]: 'table',
  },
}))(MyComponent);

// good
export default withStyles(({ fallback }) => ({
  muscles: {
    display: 'flex',
  },

  muscles_fallback: {
    [fallback('display')]: 'table',
  },
}))(MyComponent);
Create as few custom themes as possible — many applications only need one. Namespace custom theme settings under a nested object with a unique and descriptive key.
// bad
ThemedStyleSheet.registerTheme('mySection', {
  mySectionPrimaryColor: 'green',
});

// good
ThemedStyleSheet.registerTheme('mySection', {
  mySection: {
    primaryColor: 'green',
  },
});

Build docs developers (and LLMs) love