Skip to content
Theme UI
GitHub

TypeScript

Theme UI is written in TypeScript.

While most APIs in Theme UI should just work in TypeScript, there are a few advanced use cases which will differ slightly. This guide is intended to cover those use cases.

Exact theme type

The Theme type represents all possible themes.

It might be what you need when you're writing a library of reusable components or an app where the theme object is provided by the user and kept in the database. However, it's not the most convenient way to think about the theme, when building a blog or a landing page.

To know the exact type of your particular theme on type level, you can use an identity "constructor" function to narrow the type.

const makeTheme = <T extends Theme>(t: T) => t
const theme = makeTheme({
colors: {
background: 'white',
text: 'black',
blue: {
light: '#187abf',
dark: '#235a97',
},
},
})
export type ExactTheme = typeof theme
// No error
const lightBlue = theme.colors.blue.light

You can then reexport useThemeUI hook with narrowed type.

import { ThemeUIContextValue } from 'theme-ui'
interface ExactContextValue extends Omit<ThemeUIContextValue, 'theme'> {
theme: ExactTheme
}
export const useTheme = (useThemeUI as unknown) as () => ExactContextValue

Try it on CodeSandbox.

Accessing optional properties

Because all properties of Theme are optional, accessing them requires usage of get function.

/** @jsxImportSource theme-ui */
import { Theme } from 'theme-ui'
const theme: Theme = {
space: [0, 8, 16, 32, 64, 128, 256],
sizes: [0, 8, 16, 32, 64, 128, 256],
}
// Type error on `theme.space` and `theme.sizes`.
// Object is possibly 'undefined'.ts(2532)
return <div sx={{ size: (t) => t.space[3] + t.sizes[5] }} />
/** @jsxImportSource theme-ui */
import { Theme, get } from 'theme-ui'
// No error
return <div sx={{ size: (t) => get(t, 'space.3') + get(t, 'sizes.5') }} />

Properties of scales can be accessed with optional chaining. Values under numeric keys can be accessed with bracket notation.

/** @jsxImportSource theme-ui */
const parse = (x: unknown) => parseInt(String(x))
return (
<div sx={{
size: size: (t) => parse(t.space?.[3]) + parse(t.sizes?.[5]) }}
/>
)

Try it on CodeSandbox.

Additional properties in the theme

If you need additional properties in theme object, you can add them with declaration merging.

interface MySyntaxHighlightingTheme {
foreground: string
}
declare module 'theme-ui' {
interface Theme {
syntaxHighlighting: MySyntaxHighlightingTheme
}
}
const theme: Theme = {
syntaxHighlighting: {
foreground: '#222',
},
}
const syntaxHighlighting = theme.syntaxHighlighting

Try it in TypeScript Playground.

Common Problems

Union types are not inferred without explicit annotation

Style objects defined outside of css function and sx prop need explicit annotation to prevent following error.

/** @jsxImportSource theme-ui */
const style = { whiteSpace: 'pre-line' }
// Type '{ whiteSpace: string; }' is not assignable to type 'ThemeUICSSObject'.
// Type 'string' is not assignable to type '"inherit" | "initial" | "revert" | "unset" | "normal" | "break-spaces"
return <div sx={style} />

Try it on CodeSandbox.

TypeScript assumes that whiteSpace here is a string, but the whiteSpace property in ThemeUICSSObject is a union of possible white-space values (see on MDN) or a nested style object.

We can explicitly annotate style ensure that it is a correct Theme UI style object and that whiteSpace is one of appropriate values.

/** @jsxImportSource theme-ui */
import { ThemeUICSSObject } from 'theme-ui'
const style: ThemeUICSSObject = { whiteSpace: 'pre-line' }
// No error
return <div sx={style} />

Try it on CodeSandbox.

We could also fix our problem by narrowing the type of style with a const assertion.

const style = { whiteSpace: 'pre-line' } as const

This is succinct, but error prone, because we won't get TS intellisense support inside of this object.

Edit the page on GitHub
Previous:
Merging Themes
Next:
Recipes