Skip to main content
Theme UI is written in TypeScript and provides comprehensive type definitions. This guide covers TypeScript setup and advanced type customization.

Requirements

Theme UI v0.16+ requires TypeScript 5.1.2 or newer and @types/react published after June 1, 2023.
npm install typescript@latest @types/react@latest
This requirement exists due to breaking changes in JSX types. See GitHub issue #2430 for details.

Basic Setup

tsconfig.json

Configure your TypeScript compiler for Theme UI:
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "theme-ui",
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true
  }
}
The JSX Automatic Runtime (react-jsx) is highly encouraged to minimize friction and avoid type errors.

File-level JSX Import Source

Override the default JSX import source per file:
/** @jsxImportSource theme-ui */

export function Component() {
  return <div sx={{ color: 'primary' }}>Hello</div>
}

Type Definitions

SxProp Type

The SxProp interface adds the sx prop to components:
import { Interpolation } from '@emotion/react'
import { ThemeUIStyleObject, Theme } from '@theme-ui/css'

export interface SxProp {
  /**
   * The sx prop lets you style elements inline, using values from your
   * theme.
   */
  sx?: ThemeUIStyleObject
  
  /**
   * Theme UI uses Emotion's JSX function. You can pass styles directly
   * using the css prop.
   */
  css?: Interpolation<Theme>
}

ThemeUIStyleObject

The main type for style objects:
import type { ThemeUIStyleObject } from 'theme-ui'

const styles: ThemeUIStyleObject = {
  color: 'primary',
  padding: 3,
  fontSize: [2, 3, 4], // Responsive array
  '&:hover': {
    color: 'secondary',
  },
}

Theme Type

The complete theme interface:
import type { Theme } from 'theme-ui'

const theme: Theme = {
  colors: {
    text: '#000',
    background: '#fff',
    primary: '#0066cc',
  },
  space: [0, 4, 8, 16, 32, 64],
  fonts: {
    body: 'system-ui, sans-serif',
    heading: 'Georgia, serif',
  },
  fontSizes: [12, 14, 16, 20, 24, 32, 48],
}

Extending Theme Types

Customize the theme type to add autocomplete for your specific theme structure.

Method 1: Module Augmentation

Extend the global theme type:
import type { Theme as ThemeUITheme } from 'theme-ui'

export const theme = {
  colors: {
    text: '#000',
    background: '#fff',
    primary: '#0066cc',
    // Custom colors
    accent: '#ff6b6b',
    success: '#51cf66',
    warning: '#ffd43b',
    danger: '#ff6b6b',
  },
  // Custom button variants
  buttons: {
    primary: { /* ... */ },
    danger: { /* ... */ },
    ghost: { /* ... */ },
  },
} as const

type CustomTheme = typeof theme

// Augment the module
declare module 'theme-ui' {
  interface UserThemes {
    default: CustomTheme
  }
}

export type Theme = CustomTheme
Now TypeScript will autocomplete your custom theme values:
import { theme } from './theme'

// TypeScript knows about 'accent', 'success', etc.
<div sx={{ color: 'accent' }} />

// Autocomplete for custom button variants
<Button variant="ghost" />

Method 2: Strict Theme Type

Create a strictly typed theme from scratch:
import type { Theme as BaseTheme } from 'theme-ui'

interface MyTheme extends BaseTheme {
  colors: {
    text: string
    background: string
    primary: string
    secondary: string
    accent: string
  }
  buttons: {
    primary: object
    secondary: object
    outline: object
  }
}

export const theme: MyTheme = {
  colors: {
    text: '#000',
    background: '#fff',
    primary: '#0066cc',
    secondary: '#cc0066',
    accent: '#ff6b6b',
  },
  buttons: {
    primary: {
      color: 'white',
      bg: 'primary',
    },
    secondary: {
      color: 'white',
      bg: 'secondary',
    },
    outline: {
      color: 'primary',
      bg: 'transparent',
      border: '2px solid',
      borderColor: 'primary',
    },
  },
}

declare module 'theme-ui' {
  interface UserThemes {
    default: MyTheme
  }
}

Component Types

Adding sx to Custom Components

import { SxProp } from 'theme-ui'

interface CardProps extends SxProp {
  title: string
  children: React.ReactNode
}

export function Card({ title, children, sx }: CardProps) {
  return (
    <div sx={{ padding: 3, borderRadius: 2, ...sx }}>
      <h3>{title}</h3>
      {children}
    </div>
  )
}

// Usage - TypeScript knows about sx prop
<Card title="Hello" sx={{ bg: 'primary' }}>
  Content
</Card>

Box Component Props

import { BoxProps } from 'theme-ui'

interface CustomBoxProps extends BoxProps {
  highlight?: boolean
}

export function CustomBox({ highlight, ...props }: CustomBoxProps) {
  return (
    <div
      {...props}
      sx={{
        ...props.sx,
        bg: highlight ? 'accent' : 'background',
      }}
    />
  )
}

Themed Component Type

Type definition for Themed components from @theme-ui/mdx:
import type { ThemedComponent } from '@theme-ui/mdx'

// ThemedComponent accepts sx prop and HTML props
const CustomH1: ThemedComponent<'h1'> = (props) => {
  return <h1 {...props} sx={{ ...props.sx, color: 'primary' }} />
}

Responsive Style Values

TypeScript supports responsive arrays:
import type { ResponsiveStyleValue } from 'theme-ui'

type ResponsiveColor = ResponsiveStyleValue<string>

// All of these are valid
const color1: ResponsiveColor = 'primary'
const color2: ResponsiveColor = ['primary', 'secondary']
const color3: ResponsiveColor = ['primary', null, 'accent'] // null skips breakpoint

Theme-aware Function Types

Style values can be functions that receive the theme:
import type { Theme } from 'theme-ui'

<div
  sx={{
    // Function receives typed theme
    color: (theme: Theme) => theme.colors?.primary,
    
    // With destructuring
    fontSize: ({ fontSizes }) => fontSizes?.[3],
    
    // Nested functions
    '&:hover': (theme) => ({
      color: theme.colors?.secondary,
    }),
  }}
/>

Utility Types

Scale Types

import type { Scale, ScaleDict } from '@theme-ui/css'

// Array or object scale
type SpaceScale = Scale<number | string>

const space1: SpaceScale = [0, 4, 8, 16, 32]
const space2: SpaceScale = {
  small: 4,
  medium: 8,
  large: 16,
}

Nested Scales with __default

import type { ObjectWithDefault } from '@theme-ui/css'

interface ColorScale extends ObjectWithDefault<string> {
  light?: string
  dark?: string
}

const theme = {
  colors: {
    primary: {
      __default: '#0066cc',
      light: '#3399ff',
      dark: '#004080',
    },
  },
}

// Using 'primary' resolves to __default value
<div sx={{ color: 'primary' }} /> // Uses #0066cc
<div sx={{ color: 'primary.light' }} /> // Uses #3399ff

Common Type Errors

Error: Property ‘sx’ does not exist

Ensure @jsxImportSource theme-ui is at the top of your file:
/** @jsxImportSource theme-ui */

// Now sx prop is available
export function Component() {
  return <div sx={{ color: 'primary' }} />
}

Error: Type ‘string’ is not assignable to type ‘never’

This happens when TypeScript can’t infer theme types. Add explicit types:
import type { Theme } from 'theme-ui'

const theme: Theme = {
  colors: {
    primary: '#0066cc' as const, // Add 'as const' for literal types
  },
}

Error: Index signature is missing

When accessing theme values dynamically:
// Instead of this
const getColor = (name: string, theme: Theme) => theme.colors[name]

// Use this
const getColor = (name: string, theme: Theme) => {
  return theme.colors?.[name as keyof typeof theme.colors]
}

Advanced Patterns

Discriminated Union for Variants

type ButtonVariant = 'primary' | 'secondary' | 'outline'

interface ButtonProps {
  variant?: ButtonVariant
  children: React.ReactNode
}

const buttonStyles: Record<ButtonVariant, ThemeUIStyleObject> = {
  primary: { color: 'white', bg: 'primary' },
  secondary: { color: 'white', bg: 'secondary' },
  outline: { color: 'primary', bg: 'transparent' },
}

export function Button({ variant = 'primary', children }: ButtonProps) {
  return <button sx={buttonStyles[variant]}>{children}</button>
}

Generic Themed Component

import type { ComponentProps, ElementType } from 'react'
import type { SxProp } from 'theme-ui'

interface PolymorphicProps<T extends ElementType> extends SxProp {
  as?: T
}

type Props<T extends ElementType> = PolymorphicProps<T> &
  Omit<ComponentProps<T>, keyof PolymorphicProps<T>>

function ThemedBox<T extends ElementType = 'div'>({
  as,
  sx,
  ...props
}: Props<T>) {
  const Component = as || 'div'
  return <Component sx={{ padding: 3, ...sx }} {...props} />
}

// Usage
<ThemedBox>Div element</ThemedBox>
<ThemedBox as="section">Section element</ThemedBox>
<ThemedBox as="a" href="/">Link element</ThemedBox>

Const Assertion for Strict Typing

export const theme = {
  colors: {
    modes: {
      dark: {
        text: '#fff',
        background: '#000',
      },
    },
  },
  buttons: {
    primary: {
      color: 'white',
      bg: 'primary',
    },
  },
} as const

type Theme = typeof theme

// Now TypeScript knows the exact structure
declare module 'theme-ui' {
  interface UserThemes {
    default: Theme
  }
}

Migration from JavaScript

Converting a JavaScript theme to TypeScript:
export const theme = {
  colors: {
    text: '#000',
    primary: '#0066cc',
  },
  space: [0, 4, 8, 16],
}

Resources

TypeScript Handbook

Official TypeScript documentation

Emotion TypeScript

TypeScript guide for Emotion

Theme UI GitHub

Source code and type definitions

Migration Guide

Upgrading to latest version

Build docs developers (and LLMs) love