Compiled

Limitations

The architecture or design of Compiled prevents some things that are possible with runtime libraries. There are also some features we would like to add to Compiled in the future that we have not yet.

Dynamic selectors

Styles with dynamic selectors can't be created (at runtime or extraction) and may result in missing styles. If the variable is a local static value instead it should work!

/** @jsxImportSource @compiled/react */
import { css } from '@compiled/react';
const selectedIndex = useState(0);

const styles = css({
  // Will not work!
  [`:nth-child(${selectedIndex})`]: {
    display: 'block',
  },
});

<div css={styles} />;

Mixing dynamic styles and indirect selectors

You cannot mix dynamic styles and indirect selectors (eg. div + label or div ~ label) as the dynamic value will not be applied to the indirect selector.

Example, this will result in label not having the inline props.margin applied as it will not have access to the CSS variable we inject:

This should throw an error

export default (props) => {
  const styles = css({
    '& + label': {
      margin: `8px ${props.margin}`,
    },
  });

  return (
    <div>
      <span css={styles}>Hello</span>
      <label>world</label>
    </div>
  );
};

This might happen with static code in certain scenarios when parsed by Babel in ways our Compiled plugin doesn't handle, best to inspect your code (even in Babel pre-Compiled transformation) if you receive this error.

Overlapping styles, caused by media queries and other at-rules

Media queries and other at-rules are sorted deterministically when stylesheet extraction is turned on. See this page for more details of how the sorting works and what its limitations are.

Returning static values from a dynamic property

Styled components that use functions to dynamically assign properties, such as in the example below, may face issues where these properties are incorrectly resolved to a static value based on the first return statement:

const Component = styled.li({
  boxShadow: (props) => {
    if (props.isSelected) {
      return '0 0 0 1px magenta';
    }
    return '0 0 0 1px green';
  },
});

In this scenario, the result will always include a box shadow with the first static value '0 0 0 1px magenta', and the function itself will not be executed at runtime. Consequently, any logic defined within the function will be ignored. This behavior occurs specifically when the function's first return statement provides a static value (e.g., a plain string).

The simplest workaround is migrating to a ternary, eg. props.isSelected ? '0 0 0 1px magenta' : '0 0 0 1px green', but in general the usage of dynamic styles is effectively deprecated, see no-dynamic-styles eslint rule for better practices.

For more details, refer to the Github Issue.

Unsupported features

Below is a non-exhaustive list of features that Compiled does not support. Some are features we would like to add to Compiled at some point in the future, while others are features that we don't plan to implement.

Theming

We've elected not to support styled-components-style theming in Compiled, as the implementation complexity is high, and the same effect can be achieved with CSS variables.

See here for the original (no longer pursued) GitHub proposal.

Component selectors

We currently have no plans to support component selectors in Compiled. This is due to Compiled's atomic CSS design, where every style used is its own CSS class name. Component selectors would negate the benefits of atomic CSS in reducing stylesheet size.

This can feel limiting in some situations, for example if you need styles to apply to a child component depending on a parent component's state (which is an issue that other libraries face as well). Below is an example of this:

import React from 'react';
import styled from 'styled-components';

const List = styled.div`
  font-size: 2rem;
`;

const Item = styled.div`
  color: green;

  /* If List is hovered, then its children Items will turn red. */
  ${List}:hover & {
    color: red;
  }
`;

function App() {
  return (
    <List>
      <Item>Item 1</Item>
      <Item>Item 2</Item>
      <Item>Item 3</Item>
    </List>
  );
}

While we don't currently have a first-class API to handle this use case, there are two potential approaches you can use to write this in Compiled.

The recommended and more idiomatic way is to use JavaScript to pass state between the parent and child elements. There are many ways to achieve this – below is just one approach:

/** @jsxImportSource @compiled/react */
const { css } from '@compiled/react';

const listStyles = css({ fontSize: '2rem' });
const itemStyles = css({ color: 'green', });
const redStyles = css({ color: 'red' });

const List = ({ children, hoverCallback }) => (
  <div
    css={listStyles}
    onMouseEnter={() => hoverCallback(true)}
    onMouseLeave={() => hoverCallback(false)}
  >
    {children}
  </div>
);

const Item = ({ children, isHover }) => (
  <div css={[itemStyles, isHover && redStyles]}>
    {children}
  </div>
);

function App() {
  const [isHover, setIsHover] = React.useState(false);
  return (
    <List hoverCallback={setIsHover}>
      <Item isHover={isHover}>Item 1</Item>
      <Item isHover={isHover}>Item 2</Item>
      <Item isHover={isHover}>Item 3</Item>
    </List>
  );
}

If the first method does not work for your use case, or if it degrades runtime performance to an unacceptable level, a workaround is to use a data attribute as a selector in your CSS. This is faster at runtime, but it negates the benefits of atomic CSS by bloating your stylesheet size (as mentioned earlier). In addition, the styles are much harder to statically analyse for any tooling from Compiled or the Atlassian Design System:

/** @jsxImportSource @compiled/react */
const { css } from '@compiled/react';

const listStyles = css({ fontSize: '2rem' });
const itemStyles = css({
  color: 'green',
  '[data-component-selector="list-84d2"]:hover &': {
    color: 'red',
  },
});

const List = ({ children }) => (
  <div data-component-selector="list-84d2" css={listStyles}>
    {children}
  </div>
);

const Item = ({ children }) => (
  <div css={itemStyles}>
    {children}
  </div>
);

function App() {
  return (
    <List>
      <Item>Item 1</Item>
      <Item>Item 2</Item>
      <Item>Item 3</Item>
    </List>
  );
}

Other unsupported features

Found a bug?

When migrating over you may find some code that doesn't work as expected — in that case please raise an issue and we'll look into it. Bonus points if you put up a pull request as well!

Suggest changes to this page ➚