6 Tips for Styling with Grommet

Recently, I’ve been using Grommet as a React component & theming library. It’s been a good development experience so far, and there are some compelling tools for it, such as the designer and themer. We haven’t used them much, but it’s exciting to see the work the Grommet team has put in.

One of the major pain points of using Grommet is the lack of documentation. However, there is a comprehensive Storybook library that helps to fill the gap. With that said, in the Storybook stories, it can be difficult to parse whether a particular strategy is recommended or if it’s just convenient for the example.

After using Grommet for a few months, I have collected a few recommendations that have improved our experience.

Don’t declare the entire theme.

When we first started using Grommet, we used the theme designer to generate our app theme. This ended up being challenging to maintain, as we couldn’t determine whether a value was intentionally declared or just an output of the theme designer.

We ended up spending some time paring it down, and it’s much easier to maintain. Additionally (and likely due to the beta status of the theme designer), there were some conflicts in the JSON output that prevented colors from being set on certain components.

Prioritize the Grommet API, not custom CSS or inline styles.

Using the <Grommet theme={theme}> component and setting a theme allows for staying within the Grommet ecosystem. Then, it simplifies using components and styles within Grommet, such as <Button primary label="Hello!" />, <Button secondary label="Hello!" />, or <Text color="a-custom-color-defined-in-the-theme">Hello!</Text>. However, there are times when custom CSS is necessary. In that case, Grommet recommends styled-components.

Use the Grommet theme to reference colors, but normalize colors before using.

It’s best to use named Grommet colors throughout the application, rather than hex values. Additionally, colors should declare the intent, rather than the appearance. In other words, use named colors like brand or text, not dark-green or lightest-grey.

Colors can change, and it’s frustrating to track down all occurrences of dark-green, but well-thought-out themes will adapt to those changes. We can use named colors in the Grommet theme and components but need to normalize them before using them in other components or CSS.

Consider the following component:

export const GrommetThemeExample = () => {
  const customTheme: ThemeType = {
    global: {
      colors: {
        control: "lime-green",
        text: "off-white",
        "off-white": "#FFFFF0",
        "background-back": "royal-blue",
        "lime-green": "#ABC123",
        "royal-blue": "#123ABC",
      },
    },
  };
  return (
    <Grommet theme={customTheme}>
      <Box pad="medium">
        <Text color="background-back">This element uses Grommet colors </Text>
        <div style={{ color: "background-back" }}>
          "background-back" is not a valid color, so this won't work
        </div>
      </Box>
    </Grommet>
  );
};

For the Text component, Grommet will normalize the color to #123ABC. However, the div will not successfuly set the color, because "background-back" is not a valid color. To do that, we need to use the normalizeColor helper that Grommet provides.

import { normalizeColor } from "grommet/utils";

const ChildComponentWithGrommetThemeContext: React.FC<{}> = (props) => {
  const theme = useContext<ThemeType>(ThemeContext);
  const backgroundColor = normalizeColor("background-back", theme);
  const textColor = normalizeColor("text", theme);

  return (
    <Box margin="large">
      <div style={{ backgroundColor: backgroundColor, color: textColor }}>
        This is a custom element that uses the normalized color from the
        ThemeContext
      </div>
    </Box>
  );
};

If you use this component under a Grommet tag, it has access to the theme context. I recommend passing in the theme from the context, as that is the full application theme, rather than the partial theme we provide (see recommendation #1).

Use styled-components.

Because of the tight integration with styled-components, we can access the theme directly in the CSS. In the previous example, we manually retrieved the theme from the context, but it’s easy with styled-components.

const CustomDivWithGrommetStyledComponents = styled.div`
  ${({ theme }) => css`
    background-color: ${normalizeColor("control", theme)};
    color: ${normalizeColor("text", theme)};
  `}
`;
`;

Additionally, we can use styled-components when declaring a Grommet theme!

  const customTheme: ThemeType = {
    global: {
      colors: {
        control: "lime-green",
        text: "off-white",
        "off-white": "#FFFFF0",
        "background-back": "royal-blue",
        "lime-green": "#ABC123",
        "royal-blue": "#123ABC",
      },
    },
    button: {
      primary: {
        background: "background-back",
        border: {
          width: "10px",
        },
      },
      // We can use styled-components within the Grommet app theme
      extend: ({ theme }) => css`
        height: 50px;
        border-color: ${getNormalizedColor(theme, "control")};
      `,
    },
  };

Prioritize Grommet theme > styled-components > inline styles.

The last few points show the benefits of staying within the Grommet ecosystem and the excellent integration with styled-components. If a component can be styled within the Grommet theme, that’s the best place to make that change. If a pattern shouldn’t be replicated across the whole theme or a non-Grommet component needs to be styled, then styled-components is the tool for that. It’s best to avoid using React inline styles.

Nested <Grommet> tags are supported, and with the deepMerge utility provided, that’s a good option for localized styles that shouldn’t be replicated app wide.

export const AppThemingWithGrommetTheme = () => {
  const theme: ThemeType = {
    global: {
      colors: {
        text: "#00FF00",
      },
    },
  };
  const customTheme: ThemeType = {
    global: {
      colors: {
        text: "#FF0000",
      },
    },
  };
  return (
    <Grommet theme={theme}>
      <Text color="text">This element uses Grommet colors </Text>
      <Grommet theme={deepMerge(theme, customTheme)}>
        <Text color="text">This element uses a nested Grommet tag</Text>
      </Grommet>
    </Grommet>
  );
};

Use the grid system.

One of my favorite things about Grommet is the grid system. It’s easy to understand, and more importantly, it’s easy to build out beautiful pages.

const GridExample: React.FC = () => {
  return (
    <Grid
      fill
      rows={["auto", "flex"]}
      columns={["auto", "flex"]}
      areas={[
        ["sidebar", "header"],
        ["sidebar", "main"],
        ["sidebar", "footer"],
      ]}
    >
      <Box gridArea="header"></Box>
      <Box gridArea="main"></Box>
      <Box gridArea="sidebar"></Box>
    </Grid>
  );
};

Overall, I’ve been enjoying Grommet. I would recommend it to anyone looking for a native React (not CSS) library or something with a different look than Bootstrap or Material UI. Let me know in the comments if you have other tips for working with Grommet!

Conversation
  • Hi there Dan!

    Thanks for writing this article.

    As someone beginning to work with Grommet it helped me a lot to understand the best ways to styling with it.

    By the way, I am a bit lost when it comes to dark/light theming with grommet. Do you know where are some good docs about this or an example app?

    Thanks and have a great day!

  • Comments are closed.