RN Neo

Customization

Ways to customize the theme and the components

Customization

rn-neo is built to be customized. You can change colors, tweak spacing and radius scales, swap fonts, toggle shadows and borders globally — or override any of it at the component level on the fly.

Customization works through two props on NeoProvider: theme and config.

<NeoProvider theme={myTheme} config={myConfig}>
  <App />
</NeoProvider>

Theme vs Config

These two props have different responsibilities:

  • theme controls the visual identity — colors, dark mode flag, and global defaults for border, shadow, radius, and animation behavior.
  • config controls the design system scale — the actual pixel values behind every token (sm, md, lg, etc.) for spacing, radius, border size, shadow size, and font sizes. It also maps font weight names to font family strings.

Both are optional and support partial overrides — you only need to provide what you want to change. Everything else falls back to the built-in defaults.


Built-in Themes

Two themes ship with rn-neo:

import { DefaultLightTheme, DefaultDarkTheme } from 'rn-neo';

DefaultLightTheme is used automatically if you don't pass a theme prop at all. To use the dark theme, pass it explicitly:

<NeoProvider theme={DefaultDarkTheme}>
  <App />
</NeoProvider>

Dark Mode

rn-neo does not manage dark mode automatically. You decide when to switch themes and how to detect the user's preference. A common pattern using React Native's useColorScheme:

import { useColorScheme } from 'react-native';
import { NeoProvider, DefaultLightTheme, DefaultDarkTheme } from 'rn-neo';

export default function App() {
  const scheme = useColorScheme();
  const theme = scheme === 'dark' ? DefaultDarkTheme : DefaultLightTheme;

  return (
    <NeoProvider theme={theme}>
      <YourApp />
    </NeoProvider>
  );
}

Customizing the Theme

The theme prop accepts a NeoTheme object. Every field is optional — spread the built-in theme and override only what you need.

import { DefaultLightTheme } from 'rn-neo';
import type { NeoTheme } from 'rn-neo';

const myTheme: NeoTheme = {
  ...DefaultLightTheme,
  colors: {
    ...DefaultLightTheme.colors,
    primary: '#6600FF',
    onPrimary: '#FFFFFF',
  },
  radius: 'md',
  shadowSize: 'lg',
  animation: false,
};

NeoTheme Reference

PropTypeDefaultDescription
darkbooleanfalseMarks the theme as dark. Used internally.
colorsColorTokensFull color token map.
radiusRadius'none'Default radius for all components.
borderSizeBorderSize'medium'Default border width for all components.
shadowSizeShadowSize'md'Default shadow offset for all components.
borderbooleantrueShow borders globally by default.
shadowbooleantrueShow shadows globally by default.
animationbooleantrueEnable animations globally.
animationDurationnumber100Duration for animations in ms.

Color Tokens

Colors are defined as a flat map of semantic token names to hex strings. Every token has a foreground counterpart (e.g. primary and onPrimary) that components use for text and icons rendered on top of that color.

TokenPairUsage
primaryonPrimaryMain brand color
secondaryonSecondaryAccent color
erroronErrorDestructive states
successonSuccessPositive feedback
warningonWarningCautionary states
backgroundonBackgroundScreen background
surfaceonSurfaceCard and container backgrounds
borderDefault border color
shadowDefault shadow color
mutedSubdued text and icons

Customizing the Config

The config prop accepts a NeoConfig object. It defines the concrete pixel values for every design token used across the library, and maps font weight names to font families.

Like theme, it supports partial overrides.

import type { NeoConfig } from 'rn-neo';

const myConfig: NeoConfig = {
  spacing: {
    'xs': 4,
    'sm': 8,
    'md': 12,
    'lg': 16,
    'xl': 20,
    '2xl': 28,
    '3xl': 36,
  },
  fonts: {
    normal: 'Inter-Regular',
    medium: 'Inter-Medium',
    bold: 'Inter-Bold',
  },
};

<NeoProvider config={myConfig}>
  <App />
</NeoProvider>;

NeoConfig Reference

Radius tokens (Radius)

TokenDefault (px)
none0
sm4
md8
lg12
xl16
full999999

Border size tokens (BorderSize)

TokenDefault (px)
none0
thin1
medium2
thick3

Shadow size tokens (ShadowSize)

TokenDefault (px)
sm2
md4
lg8

Spacing tokens (Spacing)

TokenDefault (px)
xs2
sm4
md8
lg12
xl18
2xl24
3xl30

Font size tokens (FontSize)

TokenDefault (px)
sm12
md14
lg16
xl20
2xl24
3xl28

Font weight tokens (FontWeight)

Maps weight names to font family strings. The library uses these internally when rendering text with a specific fontWeight prop.

TokenDefault
normal'System'
medium'System'
bold'System'

To use custom fonts, link them to your project first (via the Expo plugin or npx react-native-asset for bare React Native), then pass their names here:

fonts: {
  normal: 'SpaceGrotesk-Regular',
  medium: 'SpaceGrotesk-Medium',
  bold: 'SpaceGrotesk-Bold',
}

Component-Level Overrides

Every style prop you pass directly to a component takes priority over the theme default. You don't need to touch the theme to change how one specific component looks.

// Uses theme defaults
<Box />

// Overrides radius and shadow for this instance only
<Box radius="lg" shadowSize="sm" shadowColor="primary" />

// Disables border for this button only
<Pressable border={false}>
  <Text>Click me</Text>
</Pressable>

This works for border, borderSize, borderColor, radius, shadow, shadowSize, shadowColor, backgroundColor, and all spacing props across every component that supports them.


useNeo()

The useNeo hook gives you direct access to the active theme and config anywhere inside your component tree. Useful when you're building custom components and want them to stay consistent with the rest of your UI.

import { useNeo } from 'rn-neo';

function MyComponent() {
  const { theme, config } = useNeo();

  return (
    <View
      style={{
        backgroundColor: theme.colors.surface,
        padding: config.spacing.md,
        borderRadius: config.radius.lg,
      }}
    />
  );
}

theme gives you the full resolved NeoTheme and config gives you the full resolved NeoConfig — both with your overrides already merged in.

On this page