CSS Hooks
Documentation
Source on GitHub

Migrating to v2

v2 introduces a more composable model for hooks, adding flexibility and promoting reuse. It also makes some future-proofing changes to the way you configure and set up hooks. Let's dive in!

Configuration

Previously, the createHooks function accepted two arguments: the first one declaring the hooks, and the second passing configuration options. In v2, these have been merged into a single configuration object. Some options have changed as well.

Merging hook declarations and configuration options

Pass a single object to the createHooks function. Move hook declarations under the hooks field, and add any configuration options that were previously passed in the second argument.

Before

// src/css.ts

export const [hooks, css] = createHooks(
  {
    "&:hover": "&:hover",
  },
  {
    fallback: "revert-layer",
    debug: true,
  },
);

After

// src/css.ts

export const { styleSheet, css } = createHooks({
  hooks: {
    "&:hover": "&:hover",
  },
  fallback: "revert-layer",
  debug: true,
});

Updating the fallback option

In v2, the default value of the fallback option changed from "unset" to "revert-layer". If you're concerned about compatibility with older browsers, you may want to consider setting this option:

fallback: "unset",

For more information, see the Configuration guide.

Updating the sort option

If you previously used sort: true, you can simply remove that option, as it is now enabled by default.

Otherwise, you can add the following to your configuration to disable sorting, which most closely resembles the default behavior in v1:

sort: {
  properties: false,
  conditionalStyles: false
}

For more information about sorting, please review the Configuration guide.

Setup

In v1, the createHooks function returned a tuple containing (1) a CSS string (the style sheet needed to support the configured hooks) and (2) the css function used to create inline style rules. In v2, it returns an object instead. This can be destructured in a similar manner.

Destructuring the createHooks return value

Change the array (tuple) destructuring syntax to use object destructuring syntax instead. Note that what was previously called hooks (by convention) has been renamed to styleSheet.

Before

// src/css.ts

export const [hooks, css] = createHooks(/* ... */);

After

// src/css.ts

export const { styleSheet, css } = createHooks(/* ... */);

Adding the style sheet

Find where the style sheet is rendered, e.g. in your root component. Update the hooks import to the new name styleSheet, and invoke it as a function to render the CSS string.

Before

// src/app.tsx

import { hooks } from "./css";

export function App() {
  return (
    <>
      <style dangerouslySetInnerHTML={{ __html: hooks }} />
      <HomePage />
    </>
  );
}

After

// src/app.tsx

import { styleSheet } from "./css";

export function App() {
  return (
    <>
      <style dangerouslySetInnerHTML={{ __html: styleSheet() }} />
      <HomePage />
    </>
  );
}

Usage

The most prominent change in v2 is a more advanced syntax for conditional styles, which allows hooks to be combined and reused more effectively. In v1, nesting provided the means for hook composition, but this was strictly an "and" operation. In v2, an on callback replaces nested style objects to enable "or" and "not" operations—a slightly heavier, but much more powerful, syntax.

Wherever you use the css function, you'll need to migrate to the on callback.

Note

If you want to migrate to v2 but prefer the previous API, please see css-hooks-basic.

Basic use case

Before
export function Button({ children }) {
  return (
    <button
      style={css({
        color: "blue",
        "&:hover": {
          color: "red",
        },
      })}
    >
      {children}
    </button>
  );
}
After
export function Button({ children }) {
  return (
    <button
      style={css({
        color: "blue",
        on: $ => [
          $("&:hover", {
            color: "red",
          }),
        ],
      })}
    >
      {children}
    </button>
  );
}

With compositional nesting

Before
export function Button({ children }) {
  return (
    <button
      style={css({
        color: "blue",
        "&:enabled": {
          "&:hover": {
            color: "red",
          },
        },
      })}
    >
      {children}
    </button>
  );
}
After
export function Button({ children }) {
  return (
    <button
      style={css({
        color: "blue",
        on: ($, { and }) => [
          $(and("&:enabled", "&:hover"), {
            color: "red",
          }),
        ],
      })}
    >
      {children}
    </button>
  );
}