Skip to main content

The Problem

Imagine a Button with an Icon inside. You want the icon size to match the button size. Without context, you’d do this:
If you change the button size, you have to remember to change the icon size too.

The Solution

With context, the icon knows its parent’s size automatically:

Creating a Context

Use createStyledContext() to define which variants should be shared.
The arrays define the possible values. TypeScript will infer the union types automatically.

Connecting Components

Pass the context to each component that should participate.

How It Works

When you pass variant props to the parent:
  1. The parent renders with those variants
  2. It wraps its children in a context provider
  3. Children with the same context read the values
  4. Children apply matching variants automatically

Default Variants Propagation

When the parent component has defaultVariants, children automatically inherit them—even without explicit props.
Now you can use the component without any props:
This is powerful for design systems where you want sensible defaults that cascade through the component tree.

Overriding Context

Children can override the context values when needed.
Direct props always win over context values.

Boolean Variants

For true/false variants, use the special ["boolean"] marker.
Just define true. You don’t need false: {}. When using the component, pass actual booleans:
⚠️ Use isDisabled instead of disabled to avoid shadowing the native disabled prop on elements like button or Pressable.

Variant Priority

When multiple sources provide variant values, this is the priority order (highest wins):
  1. 🥇 Props passed directly to the component
  2. 🥈 Context values from parent
  3. 🥉 defaultVariants in config

Local Variants

Not all variants need to propagate to children. Some behaviors are specific to a single component—like haptic feedback on a Button root, but not on its Text or Icon slots.

The Problem

Imagine you want your Button to have haptic feedback options, but this only makes sense for the Pressable root:

The Solution

Define variants outside of createStyledContext() to keep them local:

How It Works

  1. Context variants (variant) - Defined in createStyledContext(), propagate to all children with the same context
  2. Local variants (haptics) - Defined only in variants, stay on that component

When to Use Local Variants

Use local variants for:
  • Platform-specific behaviors (haptics, animations)
  • Root-only interactions (press effects, gestures)
  • Variants that don’t make semantic sense on children
Use context variants for:
  • Visual consistency (size, color, variant)
  • Semantic states (disabled, loading)
  • Anything children should know about

Function Composition

Local variants work great with function props. When you define onPress in a variant, it composes with any onPress passed to the component:

Shared Config

When multiple components share the same context and variants, you end up duplicating the config:
Use styledConfig() to create a single config validated against both components:
styledConfig() is an identity function — it returns the config unchanged with zero runtime overhead. It exists purely so TypeScript can validate the config against both components and give you full autocomplete.
styledConfig() uses the same type inference as styled(). No generics needed — just pass the components array and the config.

styledConfig() API Reference

Signature, parameters, and type safety details

Nested Contexts

If you nest components with the same context, the nearest parent wins.

Without Context

Not every component needs context. For standalone components, just omit it:
Use context when you have parent-child relationships. Skip it for standalone elements.

Next: Slots

Build compound components with dot notation