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:
Before (JavaScript)
After (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