theme.json Explained: Settings, Styles and Presets in WordPress
theme.json Explained: Settings, Styles and Presets in WordPress

theme.json is a configuration file in a WordPress theme that controls two things: which design options editors see in the block editor and Site Editor (settings), and how the site looks by default (styles). It defines your color palette, font sizes, spacing scale, layout widths and per-block defaults in one place, and WordPress turns those values into CSS custom properties and editor controls automatically.
WordPress introduced theme.json in version 5.8 (2021). The current schema is version 3, which works with WordPress 6.6 or later. As of October 2026, with WordPress 7.1.2 current, theme.json works with both block themes and classic themes, so it is relevant whether you are building a new block theme or tidying up an older classic one.
This guide is for developers and technically minded site owners. It explains the structure of the file, how presets work, real examples you can adapt, how version 3 changed defaults, and the mistakes that cause most theme.json bugs.
Key Takeaways
- theme.json has two main jobs: settings decide which controls editors get, and styles decide the default look.
- Presets such as colors and font sizes become CSS custom properties named like --wp--preset--color--contrast, plus utility classes such as has-contrast-color.
- The current schema is version 3 (WordPress 6.6+); it changed how default font sizes and spacing sizes interact with theme presets.
- The file works in classic themes too, which makes it the easiest first step toward a hybrid theme.
- Most bugs come from invalid JSON, the wrong version number, renamed preset slugs and using CSS syntax where WordPress expects its own preset syntax.
What theme.json does
Before theme.json, a theme configured the editor through a scatter of add_theme_support() calls, editor stylesheets and custom CSS. The Make WordPress Core post introducing the file in 5.8 described the problem it solves: "the conflicts that appear in an environment with core, theme, and user styles as well as the wide design area that comes with multiple blocks." theme.json gives those three layers a shared, structured format.
In practice, theme.json lets you:
- define brand colors, gradients, duotones, font families, font sizes, spacing sizes and shadows as presets,
- turn editor controls on or off globally or per block (for example, no custom colors, or no font-size control on buttons),
- set default styles for the whole site, for HTML elements such as links, buttons and headings, and for individual blocks,
- set content and wide layout widths,
- register custom templates and template parts, and bundle patterns from the Pattern Directory.
When a user changes colors or fonts in the Site Editor's Styles panel, WordPress stores those choices as a user layer on top of the theme's theme.json. That is why a theme can let editors customize within limits rather than with free rein. Our Site Editor guide covers the user side.
The structure of the file
A theme.json file lives in the root of the theme. The top-level keys in the version 3 reference are:
| Key | Purpose |
|---|---|
| $schema | Points to the JSON schema so code editors can autocomplete and flag errors (recommended) |
| version | The schema version you are writing for (currently 3) |
| settings | Presets and which design controls are available |
| styles | Default styles: global, per element and per block |
| customTemplates | Registers custom templates and their titles |
| templateParts | Registers template parts and the area they belong to (such as header or footer) |
| patterns | Bundles patterns from the WordPress.org Pattern Directory by slug |
The smallest valid file is just a version number. A practical starting point looks like this: { "$schema": "https://schemas.wp.org/trunk/theme.json", "version": 3, "settings": { }, "styles": { } }. The WordPress theme handbook calls the $schema line "highly recommended" because it gives "on-the-fly hints and error reporting in many code editors."
Settings: what editors are allowed to do
The settings object holds presets and feature toggles. The version 3 reference groups them into sections including appearanceTools, background, border, color, dimensions, layout, lightbox, position, shadow, spacing, typography and custom, plus a blocks object for per-block overrides.
appearanceTools
The handbook describes appearanceTools as "a catchall setting for enabling multiple other settings." Setting it to true switches on a group of design controls (such as border, spacing and related options) in one line, which is convenient for a starter theme. For tightly branded client sites, many developers prefer to leave it off and enable only the controls they want.
Color
A palette is a list of objects with a slug, a color value and a name. Example: "settings": { "color": { "palette": [ { "slug": "base", "color": "#ffffff", "name": "Base" }, { "slug": "contrast", "color": "#111111", "name": "Contrast" }, { "slug": "accent", "color": "#0a6cff", "name": "Accent" } ] } }. The handbook notes that "base" (background) and "contrast" (text) are de facto standard slugs that help content move between themes.
Useful color switches include custom (whether users can pick any color), defaultPalette (whether WordPress's built-in palette appears), defaultGradients and customGradient. To keep editors on brand, set custom to false and defaultPalette to false, so only your palette appears.
Typography, spacing and layout
Typography presets cover font families and font sizes (including fluid sizes). Spacing presets define a scale (for example small, medium, large) used for padding, margin and block gaps. The layout section sets contentSize and wideSize, which control how wide normal and "wide" aligned content can be.
Per-block settings and custom values
Inside settings.blocks you can override any setting for one block type, for example letting only the Heading block use a special display font, or removing the color controls from the Button block. The custom object is, in the handbook's words, "an object for adding custom settings, which are output as CSS custom properties." Values you put there become variables prefixed --wp--custom--, which is handy for things like a shared border radius or header height.
Presets and the CSS they generate
Every preset becomes a CSS custom property using the pattern --wp--preset--{type}--{slug}. A color with the slug contrast becomes --wp--preset--color--contrast. WordPress also generates classes such as .has-contrast-color and .has-contrast-background-color, which the editor writes into your content when someone picks that color.
Inside theme.json styles, reference presets with WordPress's own syntax: "color": { "text": "var:preset|color|contrast" }. The handbook says this syntax "works much better and always appears correctly throughout the interface in the WordPress admin. Save the CSS syntax for when you are actually writing CSS." In a stylesheet, you would write color: var(--wp--preset--color--contrast).
The practical consequence: preset slugs are a contract. Posts store classes like has-accent-color. If you rename the accent slug to brand, existing content still asks for has-accent-color and quietly loses its color.
Styles: the default look
The styles object, according to the handbook, "lets you configure settings at the global level, for individual elements, and individual blocks." It has three layers:
- Top level: site-wide color, typography and spacing, for example "styles": { "color": { "background": "var:preset|color|base", "text": "var:preset|color|contrast" } }.
- styles.elements: HTML elements such as link, button, heading and h1 to h6, for example "elements": { "link": { "color": { "text": "var:preset|color|accent" } } }.
- styles.blocks: individual blocks by name, for example "blocks": { "core/quote": { "typography": { "fontStyle": "italic" } } }.
Style variations, stored as separate JSON files in the theme's styles folder, let a single theme offer alternative palettes and typography that users can switch between in the Site Editor.
Versions 1, 2 and 3: what changed
theme.json has gone through three schema versions. Older version numbers are still accepted (some handbook examples still use version 2), but the living reference and new features assume version 3.
| Version | Key points |
|---|---|
| 1 | The original format introduced with WordPress 5.8 |
| 2 | Renamed several properties for consistency (for example settings.border.customRadius became settings.border.radius) and added features such as appearanceTools |
| 3 | Works with WordPress 6.6 or later; "adjusts preset defaults to be more consistent with one another," mainly through defaultFontSizes and defaultSpacingSizes |
The version 3 change trips people up. According to the official migration guide, when defaultFontSizes is true WordPress shows its default font sizes and prevents the theme from overriding them, and defaultSpacingSizes is true by default after switching to version 3. If your custom font sizes or spacing scale "disappear" or collide with core values after you bump the version number, set those two options explicitly. Read the theme.json migration guide before upgrading an existing theme, and keep the version 3 reference open while you work.
Using theme.json in a classic theme
The handbook states plainly that the file "works with both block and classic themes." For a classic theme, adding theme.json is the lowest-risk way to modernize:
- Start with version, $schema and a settings.color.palette that matches your existing brand CSS.
- Add font sizes and a spacing scale, and turn off custom colors and the default palette.
- Test the block editor: the color picker should now show only your brand colors.
- Check the front end carefully, because theme.json styles and layout settings can change spacing and widths on existing pages.
If you are building a theme from scratch, our walkthrough on developing a custom WordPress theme covers the surrounding pieces, and block themes vs classic themes explains the hybrid approach.
Common theme.json mistakes
- Invalid JSON. A trailing comma or a missing quote can stop the whole file from being applied. Use the $schema line and a JSON linter.
- Wrong or missing version. Copying a version 2 snippet into a version 3 file (or the reverse) can change how defaults behave.
- Renaming preset slugs. Existing content references slugs through classes; renaming breaks colors and sizes on old posts.
- Using CSS var() syntax in styles. It works, but the WordPress var:preset syntax displays correctly in the admin interface.
- Enabling everything. appearanceTools plus custom colors plus custom font sizes gives editors enough rope to break brand consistency.
- Fighting theme.json with CSS. Heavy !important overrides in style.css make Global Styles appear broken to editors. Move design decisions into theme.json instead.
- Ignoring user customizations. If a client changed colors in the Site Editor, editing theme.json may seem to "do nothing," because the user layer sits on top. Check Styles and reset if needed.
Performance and accessibility notes
Moving design decisions into theme.json can let you retire chunks of hand-written CSS, but a lighter site is not automatic. Keep the number of font families and weights small, avoid loading fonts you do not use, and check Core Web Vitals after changes; our guide to Core Web Vitals on WordPress explains what to measure.
For accessibility, choose palette pairs with sufficient contrast before you ship, since editors will combine your presets freely. Restricting the palette to tested combinations is one of the easiest ways to keep a site accessible as content grows.
Conclusion
theme.json turns a theme's design system into one structured file: settings decide what editors can do, styles decide how the site looks, and presets tie both to stable CSS variables. Use version 3, add the $schema line, treat slugs as permanent and keep design decisions out of ad hoc CSS. For more developer and owner guides, see the WordPress resource hub. If you need a theme built or refactored around theme.json, eSEOspace offers WordPress development services.
Frequently asked questions
Do I need theme.json to use the block editor?
No. The block editor works without it. But without theme.json, editors see WordPress's default palette and sizes rather than your brand presets, and you have less control over which design options appear.
Which theme.json version should I use?
Version 3 for new work, as long as your site runs WordPress 6.6 or later. As of October 2026, current WordPress is 7.1.2, so most sites qualify.
Where are Site Editor style changes saved?
In the database, as a user layer on top of the theme's theme.json. They do not edit the theme file itself, which is why they survive theme updates. Tools like the Create Block Theme plugin can write them back into theme files.
Can a child theme have its own theme.json?
Child themes are a common way to customize a parent theme without editing it, and theme.json is part of the theme layer. Test the merged result in the editor and on the front end, especially palettes and font sizes, before relying on it.
What is the difference between settings and styles?
Settings control which options are available (for example, which colors appear in the picker). Styles control what is applied by default (for example, which color the body text actually uses).
Put this into action with eSEOspace
We help businesses grow with website design that actually performs. Explore the services behind this guide:
Get a FREE Audit
We'll perform a comprehensive SEO, AEO, GEO & CRO audit of your website — completely free — and show you exactly how to outrank your competitors.
Don't have a site yet? Get in touch →
Get a FREE GEO/AEO/SEO Audit
We'll analyze your site's SEO, GEO, AEO & CRO — completely free — and show you exactly how to get found across Google and AI answers.
Don't have a site yet? Get in touch →
Great — your audit is on the way!
We'll send your free SEO/GEO/AEO/CRO audit within the next few hours. Where should we send it?
You're all set! ✓
Your free audit is being prepared — check your inbox in the next few hours. Talk soon!
On this page
- Key Takeaways
- What theme.json does
- The structure of the file
- Settings: what editors are allowed to do
- Presets and the CSS they generate
- Styles: the default look
- Versions 1, 2 and 3: what changed
- Using theme.json in a classic theme
- Common theme.json mistakes
- Performance and accessibility notes
- Conclusion
- Frequently asked questions





