Skip to main content
The gatsby-plugin-theme-ui provides seamless Theme UI integration with Gatsby, including automatic color mode support and component shadowing.

Installation

Install Theme UI and the Gatsby plugin:
npm install theme-ui @theme-ui/mdx gatsby-plugin-theme-ui @emotion/react @mdx-js/react

Basic Setup

Add the plugin to your gatsby-config.js:
module.exports = {
  plugins: ['gatsby-plugin-theme-ui'],
}
That’s it! The plugin automatically:
  • Wraps your app with ThemeUIProvider
  • Integrates MDX support
  • Prevents color mode flash on page load
  • Sets up the theme context

Creating Your Theme

Create a theme file using Gatsby’s component shadowing:
export default {
  colors: {
    text: '#000',
    background: '#fff',
    primary: '#0066cc',
    secondary: '#cc0066',
    muted: '#f6f6f6',
  },
  fonts: {
    body: 'system-ui, sans-serif',
    heading: 'Georgia, serif',
  },
  fontSizes: [12, 14, 16, 20, 24, 32, 48, 64],
  space: [0, 4, 8, 16, 32, 64, 128, 256],
  styles: {
    root: {
      fontFamily: 'body',
      lineHeight: 'body',
    },
    h1: {
      fontSize: 5,
      fontFamily: 'heading',
      fontWeight: 'bold',
    },
    h2: {
      fontSize: 4,
      fontFamily: 'heading',
      fontWeight: 'bold',
    },
    p: {
      fontSize: 2,
      lineHeight: 'body',
    },
  },
}
The theme must be the default export from src/gatsby-plugin-theme-ui/index.js.

Plugin Options

Configure the plugin with options:
module.exports = {
  plugins: [
    {
      resolve: 'gatsby-plugin-theme-ui',
      options: {
        prismPreset: 'night-owl',
        preset: '@theme-ui/preset-funk',
        injectColorFlashScript: true,
      },
    },
  ],
}

Available Options

prismPreset
string
default:"null"
The name of the preset for styling code blocks in markdown files. Available presets: 'dracula', 'duotone-dark', 'duotone-light', 'github', 'night-owl', 'nightOwl', 'oceanicNext', 'okaidia', 'prism', 'shadesOfPurple', 'ultramin', 'vsDark', 'vsLight'.
preset
string | object
default:"null"
A theme preset to use as a base. Can be a package name (string) or a theme object. The preset is merged with your shadowed theme.
injectColorFlashScript
boolean
default:"true"
Injects a script to prevent color mode flashing on page load. Set to false for AMP pages.

Using Presets

Install a Preset Package

npm install @theme-ui/preset-funk

Configure with Preset

module.exports = {
  plugins: [
    {
      resolve: 'gatsby-plugin-theme-ui',
      options: {
        preset: '@theme-ui/preset-funk',
      },
    },
  ],
}

Extend a Preset

Combine a preset with custom styles:
import { funk } from '@theme-ui/preset-funk'

export default {
  ...funk,
  colors: {
    ...funk.colors,
    // Override specific colors
    primary: '#0066cc',
    secondary: '#cc0066',
  },
}

Code Syntax Highlighting

The plugin integrates with @theme-ui/prism for syntax highlighting.

Configure Prism Preset

module.exports = {
  plugins: [
    {
      resolve: 'gatsby-plugin-theme-ui',
      options: {
        prismPreset: 'night-owl',
      },
    },
  ],
}

Available Prism Presets

  • 'dracula'
  • 'night-owl' or 'nightOwl'
  • 'okaidia'
  • 'duotone-dark'
  • 'vsDark'
  • 'shadesOfPurple'

Custom Prism Styles

Override Prism styles in your theme:
export default {
  styles: {
    pre: {
      fontFamily: 'monospace',
      fontSize: 1,
      padding: 3,
      backgroundColor: '#011627',
      borderRadius: 2,
      overflow: 'auto',
    },
    code: {
      fontFamily: 'monospace',
    },
  },
}

Color Modes

Add color modes to your theme:
export default {
  colors: {
    text: '#000',
    background: '#fff',
    primary: '#0066cc',
    modes: {
      dark: {
        text: '#fff',
        background: '#000',
        primary: '#3399ff',
      },
    },
  },
}

Color Mode Toggle

Create a color mode toggle component:
import { useColorMode } from 'theme-ui'

export function ColorModeToggle() {
  const [colorMode, setColorMode] = useColorMode()
  
  return (
    <button
      onClick={() => {
        setColorMode(colorMode === 'default' ? 'dark' : 'default')
      }}
      sx=
        padding: 2,
        bg: 'primary',
        color: 'white',
        border: 'none',
        borderRadius: 2,
        cursor: 'pointer',
      }}
    >
      Toggle {colorMode === 'default' ? 'Dark' : 'Light'} Mode
    </button>
  )
}

Prevent Color Flash

The plugin automatically injects a script to prevent color mode flashing. This script runs before React hydrates and sets the correct color mode. To disable this (e.g., for AMP pages):
module.exports = {
  plugins: [
    {
      resolve: 'gatsby-plugin-theme-ui',
      options: {
        injectColorFlashScript: false,
      },
    },
  ],
}

Custom MDX Components

Shadow the components file to customize MDX rendering:
import React from 'react'

const components = {
  // Add linked headings
  h1: (props) => (
    <h1 {...props}>
      <a href={`#${props.id}`}>{props.children}</a>
    </h1>
  ),
  h2: (props) => (
    <h2 {...props}>
      <a href={`#${props.id}`}>{props.children}</a>
    </h2>
  ),
  
  // Custom image component
  img: (props) => (
    <img
      {...props}
      sx={{
        maxWidth: '100%',
        height: 'auto',
        borderRadius: 2,
      }}
    />
  ),
}

export default components

Component Shadowing

Gatsby’s component shadowing allows you to override plugin files.

Shadow Files

You can shadow these files:
src/
└── gatsby-plugin-theme-ui/
    ├── index.js       # Theme configuration
    └── components.js  # MDX components

Example: Complete Override

import { merge } from 'theme-ui'
import { base } from '@theme-ui/presets'

const theme = merge(base, {
  colors: {
    primary: '#0066cc',
    modes: {
      dark: {
        text: '#fff',
        background: '#000',
      },
    },
  },
  styles: {
    root: {
      fontFamily: 'body',
    },
  },
})

export default theme

Extending Gatsby Themes

Extend a Gatsby theme that uses Theme UI:
import baseTheme from 'gatsby-theme-blog/src/gatsby-plugin-theme-ui'

export default {
  ...baseTheme,
  colors: {
    ...baseTheme.colors,
    text: '#111',
    background: '#fff',
    primary: '#0066cc',
  },
}

Load Theme from Custom Path

If you prefer a different location for your theme:
module.exports = {
  plugins: [
    {
      resolve: 'gatsby-plugin-theme-ui',
      options: {
        preset: require('./src/theme'),
      },
    },
  ],
}
// Note: Use module.exports for gatsby-config.js
module.exports = {
  colors: {
    text: '#000',
    background: '#fff',
    primary: '#0066cc',
  },
}
gatsby-config.js doesn’t support ES6 modules. Use module.exports instead of export default.

Plugin Implementation

Here’s how the plugin works internally:

gatsby-node.js

The plugin processes theme presets and creates a Gatsby node:
exports.onPreInit = ({ reporter }, options) => {
  let { preset, prismPreset } = options

  // Load preset if it's a string
  if (typeof preset === 'string') {
    try {
      options.preset = require(preset)
    } catch {
      reporter.warn(
        'Theme dependency not installed. Only local styles will appear.'
      )
    }
  }

  // Load Prism preset
  if (prismPreset) {
    try {
      options.prismPreset = require(prismPreset)
    } catch {
      reporter.warn('Prism dependency not installed.')
    }
  }
}

exports.sourceNodes = (
  { actions, createContentDigest },
  { preset = {}, prismPreset = {} }
) => {
  const { createNode } = actions

  // Create a node with theme config
  createNode({
    preset,
    prismPreset,
    id: 'gatsby-plugin-theme-ui-config',
    parent: null,
    children: [],
    internal: {
      type: 'ThemeUiConfig',
      contentDigest: createContentDigest({ preset, prismPreset }),
    },
  })
}

Provider Component

The plugin wraps your app with providers:
import { ThemeUIProvider, merge } from 'theme-ui'
import { useThemedStylesWithMdx } from '@theme-ui/mdx'
import { MDXProvider, useMDXComponents } from '@mdx-js/react'
import React from 'react'

import localTheme from './index'
import components from './components'
import useThemeUiConfig from './hooks/configOptions'

const Root = ({ children }) => {
  const themeUiConfig = useThemeUiConfig()
  const { preset, prismPreset } = themeUiConfig

  const theme = preset.default || preset

  // Merge with Prism preset
  const themeWithPrism = merge(theme, {
    styles: {
      pre: prismPreset,
    },
  })

  // Merge with local theme
  const fullTheme = merge(themeWithPrism, localTheme)

  return (
    <ThemeUIProvider theme={fullTheme}>
      <MDXProvider
        components={useThemedStylesWithMdx(useMDXComponents(components))}
      >
        {children}
      </MDXProvider>
    </ThemeUIProvider>
  )
}

export const WrapRootElement = ({ element }) => {
  return <Root>{element}</Root>
}

Complete Example

Here’s a complete Gatsby site setup:
module.exports = {
  plugins: [
    'gatsby-plugin-theme-ui',
    {
      resolve: 'gatsby-plugin-mdx',
      options: {
        extensions: ['.mdx', '.md'],
      },
    },
  ],
}

Troubleshooting

Theme Not Applying

Make sure your theme file exports a default:
// ✅ Correct
export default { colors: { ... } }

// ❌ Incorrect
export const theme = { colors: { ... } }

Color Flash on Load

Ensure injectColorFlashScript is true (default):
{
  resolve: 'gatsby-plugin-theme-ui',
  options: {
    injectColorFlashScript: true, // default
  },
}

Preset Not Loading

Verify the preset package is installed:
npm install @theme-ui/preset-funk

Resources

Gatsby Shadowing

Learn about component shadowing in Gatsby

Theme UI Presets

Browse available Theme UI presets

MDX in Gatsby

Using MDX with Gatsby

Prism Themes

Code syntax highlighting themes

Build docs developers (and LLMs) love