DEV Community

MemeChatAI
MemeChatAI

Posted on

Theming a React Native app in one place with NativeWind

Every app starts with one brand color in one file. Then there are nine copies of #6C4DFF scattered across buttons, a header, a loading spinner, and a settings row nobody has opened in months. The day someone asks to nudge the brand a little warmer, you are grepping for hex codes and hoping you caught them all.

We hit this building Meme Chat AI. Styles were defined per component, so the design lived in forty StyleSheet.create blocks instead of in one place. NativeWind is how we pulled it back into a single source.

What NativeWind actually is

It's Tailwind for React Native. You write utility classes in a className prop and they compile to native styles, no runtime stylesheet objects to maintain by hand.

<Pressable className="bg-primary rounded-2xl px-4 py-3">
  <Text className="text-on-primary font-semibold">Send</Text>
</Pressable>
Enter fullscreen mode Exit fullscreen mode

That's the surface change. The part that mattered more for us was where those names like bg-primary come from.

One config, every screen

The token names resolve from a single Tailwind config. Colors, spacing, radius, and font sizes all live there, and every component reads the same definitions.

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: "#6C4DFF",
        "on-primary": "#FFFFFF",
        surface: "#0E0E12",
        muted: "#9A9AA8",
      },
      borderRadius: { card: "20px" },
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Now bg-primary means the same purple in every file. When that purple changes, it changes in one line and the whole app moves with it. No component owns its own copy of the brand, so there is nothing to hunt down later.

Dark mode stops being a project

Because the theme is centralized, light and dark are two values of the same token instead of two parallel stylesheets. You mark the variant inline and NativeWind picks the right one from the system setting.

<View className="bg-white dark:bg-surface">
  <Text className="text-black dark:text-white">Memes incoming</Text>
</View>
Enter fullscreen mode Exit fullscreen mode

There's no theme-switch plumbing threaded through every component and no second set of styles to keep in sync with the first. The thing that usually turns into a multi-day retrofit became a prop.

What it bought us

Design changes that used to touch dozens of files now touch the config. New screens inherit the brand for free because they're built from the same tokens as everything else, so they look consistent without anyone enforcing it by hand. And the styles sit next to the markup they apply to, which made the components easier to read than a class name pointing off to a stylesheet elsewhere in the file.

None of this is special to a meme app. It's one config holding the design, utility classes reading from it, and dark mode falling out of the same tokens for free. The win was deciding the theme lives in exactly one place, and letting every screen borrow from it instead of keeping its own copy.

You can see where it landed in Meme Chat AI.

Top comments (0)