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