Setting up a child theme for hybrid or block themes

By Ian Svoboda on October 29, 2025

If you’ve been building with WordPress in the past, you may have used or heard of a “child theme” before. In this post I explain how child themes work and why you may (or may not) need one for your project.

When (and when not) to use a child theme

Conceptually, a child theme inherits functionality, styles, etc from a parent theme and can override or change certain things inherited from that parent theme. For instance, imagine you’re using a theme like Ollie, Powder, or Twenty Twenty Five. You want to use that theme but you have your own stylesheet and script you’d like to add in the mix.

So you upload your stylesheet and script files, open up functions.php and add an enqueue for those new assets. Which may be fine, until the theme is updated and has changes to functions.php.

If a theme updates its files and you’ve made changes to one of those files, your changes will be lost when the theme updates if that update included changes to that file. It’s generally a best practice to use auto updates for plugins and themes, many premium themes have auto update mechanisms, etc. And even if you’re not auto updating, unless you’re checking every single file being changed you can’t be sure your customizations won’t be lost.

The above point applies to both hybrid, classic, and block themes. If you’re not using a block theme you should almost definitely be using a child theme to preserve your changes from being lost.

That said, in block themes many customizations are all done inside the database directly. You go into the site editor, change a template or styles, and it makes that change in the database (i.e. it doesn’t change the actual .html template file in the theme itself). So if you’re not using version control and “keeping it simple” you may find you don’t need a child theme at all.

But if you’re doing things like:

  • Adding custom scripts, styles, and images
  • Using git to version control your theme/project files
  • Making significant customizations to existing theme templates or adding new ones
  • Building a site for someone else
  • Working on a site with multiple people
  • Using a theme made by someone else that receives updates

…then you probably should be using a child theme.

Thankfully they are simple to create for any theme you may want to use!

Anatomy of a child theme

At a basic level, a child theme only requires one file: style.css. WordPress themes all have a style.css file because that file is used to provide metadata about the theme such as its name, the author, its text-domain, etc. Here’s an example of a parent theme’s style.css comment header:

/**
 * Theme Name:        Fabled Sunset
 * Theme URI:         https://example.com/fabled-sunset
 * Description:       Custom theme description...
 * Version:           1.0.0
 * Author:            Your Name
 * Author URI:        https://example.com
 * Tags:              block-patterns, full-site-editing
 * Text Domain:       fabled-sunset
 * Domain Path:       /assets/lang
 * Tested up to:      6.4
 * Requires at least: 6.2
 * Requires PHP:      7.4
 * License:           GNU General Public License v2.0 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 */

The Theme Name key is the only one that’s actually required for any theme, but if the theme is listed in the Theme Directory there are some other keys required. You can read more about this in the Theme Handbook. Your child theme’s style.css file will also need a comment header of its own to allow it to be used in WordPress.

So if you were scaffolding out any child theme from scratch, you’d begin with a theme folder and then a style.css file inside it:

- wp-content/
  - themes/
    - my-child-theme/
      - style.css

There is one specific key that only applies to child themes: Template. Here you enter the directory name of the parent theme you want to inherit from. For instance, if we’re creating a child theme for Ollie, we’d use ollie which is the name of the theme directory. Here’s an example of the child theme I just made for my personal site:

style.css
/**
  * Theme Name: iansvo 2025
	* Author Name: Ian Svoboda
	* Template: ollie
	*
	*/

This tells WordPress that this theme is a child theme of ollie. So at this point, all of the scripts, styles, and other output from Ollie would still output just like they normally do. Boom!

Using Create Block Theme

If you’re using a block theme, the Create Block Theme plugin is indespensible. The plugin describes itself as “Development mode for WordPress” which is a pretty solid description! Create Block Theme adds additional developer functionality to the Site Editor to do a few helpful things:

  1. Save changes to your theme to your theme files
  2. Create a new block theme from scratch
  3. Create a theme variation
  4. Create a child theme based on the current active theme

So if you haven’t made a child theme yet, you can use Create Block Theme to make one for you. Nice! This can be convenient if you’re doing some simple low/no-code work or you just don’t care to make the folder and files yourself.

I previously mentioned that you can save changes to templates, styles, and more in a block theme and have those changes save to the database. But what if you want to save those changes to your child theme itself? This is a common step to take if you’re using git to version control your theme or you otherwise want to preserve customizations made in the site editor. The latter point is especially relevant if you’re building the site for someone else.

Here’s a hypothetical scenario:

You make your child theme and do some customizations to the site templates and partials. Then your client (or some other admin user) accidentally breaks one of the templates. What can you do?

The Site Editor has an option to “Reset” the template, which will restore it to how it was before the customizations were made. However, if the customizations you originally made previously aren’t saved in the theme files, the reset will not restore those and instead use the template (or partial) file contents found in your theme.

To resolve this kind of issue, you can save the changes made to templates in your child theme using Create Block Theme. This way if you do a template reset, it will use the version in your child theme instead of the parent theme. There are a number of different things that might be saved to the theme (like theme.json, images, fonts, and more) and you can choose which of these actually get saved.

This is an example of what that save screen looks like in the Site Editor:

Screenshot of the site editor showing the Create Block Theme sidebar panel with Save Fonts, Save Style Changes, and Save Template Changes selected.

If you haven’t used this before, I’d strongly suggest you experiment with it locally and even take backups where appropriate so you can see what the effect of saving some changes is. For instance, if you’ve selected Localize Images, it’s going to copy those images from the media library to an /assets folder and then use a pattern to reference the image (instead of just an img tag in the template).

Adding your own styles and scripts

If you didn’t need to add any styles, you could just leave the style.css file as is and carry on with other changes (like say, adding a script or some PHP logic). But it’s very common to include some custom CSS for things that aren’t possible or otherwise cumbersome in the block editor.

One simple example of this from my new personal site redesign is the .bg-grid class I use on the homepage hero:

style.css
.bg-grid {
	--color: var(--wp--preset--color--primary);
	--gradient: rgb( from var(--color) r g b / 0.15) 1px, transparent 1px;
	
	background-image: linear-gradient(
		to right,
		var(--gradient)
	),
	linear-gradient(
		to bottom,
		var(--gradient)
	);
	background-size: 40px 40px;
}

This class uses a linear gradient and some CSS variables to add the grid effect without using an image. I have CSS variables in place so the color can be easily changed without replicating all of the styles. I also added a few modifier classes to change the color of the grid:

style.css
.bg-grid--base {
	--color: var(--wp--preset--color--base);
}

.bg-grid--secondary {
	--color: var(--wp--preset--color--primary-alt);
}

Here’s an example of how this looks on the frontend:

Screenshot of the hero from the iansvoboda.com homepage

So while I could create a background gradient in the block editor, I can’t set a background-size value which is necessary for this to work. So custom code it is!

So now that I have some custom CSS in my child theme, I need that child theme’s stylesheet (style.css) to output on the frontend also. I can do this by adding a functions.php file to the root of my child theme and adding a bit of PHP to it. This file is a special one that all themes may have and, if present, it will be automatically loaded just be existing in the root of the child theme.

- wp-content/
  - themes/
    - my-child-theme/
      - style.css
      - functions.php
functions.php
<?php

add_action( 'wp_enqueue_scripts', function() {
	
	$is_live = !defined( 'WP_SCRIPT_DEBUG' ) || WP_SCRIPT_DEBUG !== true;
	
	wp_enqueue_style( 'child-styles', get_stylesheet_uri(), '', $is_live ? false : time() );
} );

If these styles needed to load after the parent theme’s stylesheet, then you’d need to also specify the handle of that stylesheet in the dependencies (deps). You can easily figure out the handle of any enqueued stylesheet by checking the link tag output by WordPress using View Source or Inspect Element:

<link 
  rel='stylesheet' 
  id='ollie-css' 
  href='https://iansvoboda.com/wp-content/themes/ollie/style.css?ver=6.8.3' 
  media='all' 
/>

For stylesheets, the handle is the value of the link tag’s id without the -css suffix (which is automatically added to all handles). For scripts, the suffix is -js. So we can update the enqueue like this to ensure our child theme stylesheet loads after the Ollie parent theme one:

functions.php
<?php

add_action( 'wp_enqueue_scripts', function() {
	
	$is_live = !defined( 'WP_SCRIPT_DEBUG' ) || WP_SCRIPT_DEBUG !== true;
	
	wp_enqueue_style( 
  	'child-styles', 
  	get_stylesheet_uri(), 
  	'ollie', // This can be a string or an array of deps if there's more than one.
  	$is_live ? false : time() 
	);
} );

If you’re enqueueing a script the concepts are the same. The main difference is that you’re using wp_enqueue_script and you should use get_theme_file_uri for the script. Additionally, you may want to add the script to the footer (which is often desirable for performance reasons) but that’s optional.

Here’s a example of enqueueing a scripts.js file from the child theme root directory along with the stylesheet we just added:

functions.php
<?php

add_action( 'wp_enqueue_scripts', function() {
	
	$is_live = !defined( 'WP_SCRIPT_DEBUG' ) || WP_SCRIPT_DEBUG !== true;
	
	wp_enqueue_style( 
  	'child-styles', 
  	get_stylesheet_uri(), 
  	'ollie', // This can be a string or an array of deps if there's more than one.
  	$is_live ? false : time() 
	);
	
	wp_enqueue_script(
	  'child-scripts',
	  get_theme_file_uri('scripts.js'),
	  '',
	  $is_live ? false : time() 
	);
} );

Adding PHP functions

Now that your child theme has its own functions.php file, you could place some other PHP here as well. The exact things you need to do will vary entirely on your site setup and project and you may find that you don’t need anything beyond just the enqueue hook I just showed you.

As an example though, say you wanted to add a custom class to the submit buttons for Gravity Forms. This requires adding a total of 3 different PHP hooks that all can use the same function to add the classes, which might look something like this:

functions.php
<?php

// Other functions.php contents...

add_filter( 'gform_submit_button', 'add_custom_css_classes', 10, 2 );
add_filter( 'gform_next_button',  'add_custom_css_classes', 10, 2 );
add_filter( 'gform_previous_button',  'add_custom_css_classes', 10, 2 );

/**
  * Add custom classes to Gravity Forms Buttons
  * @param string $button The button markup to alter.
  * @param array $form The form object associated with the button.
  *
  * @return string The updated button markup.
  */
function add_custom_css_classes( $button, $form ) {
	$fragment = WP_HTML_Processor::create_fragment( $button );
	$fragment->next_token();
	$fragment->add_class( 'gbp-button--primary' );
	$fragment->add_class( 'gform-theme__disable' );

	return $fragment->get_updated_html();
}

This functionality is exactly the same in all theme types also. If you add PHP to functions.php, it will automatically execute just because it’s in that file.

Overriding theme.json

Block themes have a theme.json file that provides settings presets and styles for the block and site editor. You can also override specific parts of these in your child theme as well (either manually or via Create Block Theme).

You can override specific parts of the file so you can still inherit the things you need but change the things you don’t. Additionally, you can add other things that weren’t there before!

When I recently redesigned my personal site, I wanted to override the theme color palette from Ollie with my own thing and use some custom CSS variables for the definitions. So I need to add the variables in settings.custom and then also update settings.color.palette to do so.

Here’s an example of my site’s theme.json file that does just that:

theme.json
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 3,
  "settings": {
  	"custom": {
  		"color": {
  			"neon-pink" : "#ff05fa",
  			"neon-pink-light": "#ff41da",
  			"neon-teal": "#00f2f4",
  			"purple": "#67119e",
  			"blue": "#264ee7",
  			"gray": {
  				"50": "rgb(249 250 251 / 1)",
  				"100": "#f3f4f6",
  				"200": "#e5e7eb",
  				"300": "#d1d5db",
  				"400": "#9ca3af",
  				"500": "#6b7280",
  				"600": "#4b5563",
  				"700": "rgb(55 65 81 / 1)",
  				"800": "#1f2937",
  				"900": "rgb(17 24 39 / 1)"
  			}
  		}
  	},
  	"color": {
  		"palette": [
  			{
  				"name": "Brand",
  				"slug": "primary",
  				"color": "var(--wp--custom--color--neon-pink)"
  			},
  			{
  				"name": "Brand Accent",
  				"slug": "primary-accent",
  				"color": "var(--wp--custom--color--neon-pink-light)"
  			},
  			{
  				"name": "Brand Alt",
  				"slug": "primary-alt",
  				"color": "var(--wp--custom--color--neon-teal)"
  			},
  			{
  				"name": "Brand Alt Accent",
  				"slug": "primary-alt-accent",
  				"color": "var(--wp--custom--color--gray--600)"
  			},
  			{
  				"name": "Contrast",
  				"slug": "main",
  				"color": "var(--wp--custom--color--gray--50)"
  			},
  			{
  				"name": "Contrast Accent",
  				"slug": "main-accent",
  				"color": "var(--wp--custom--color--gray--400)"
  			},
  			{
  				"name": "Base",
  				"slug": "base",
  				"color": "var(--wp--custom--color--gray--900)"
  			},
  			{
  				"name": "Base Accent",
  				"slug": "secondary",
  				"color": "var(--wp--custom--color--gray--300)"
  			},
  			{
  				"name": "Tint",
  				"slug": "tertiary",
  				"color": "var(--wp--custom--color--gray--800)"
  			},
  			{
  				"name": "Border Base",
  				"slug": "border-light",
  				"color": "var(--wp--custom--color--gray--700)"
  			},
  			{
  				"name": "Border Contrast",
  				"slug": "border-dark",
  				"color": "var(--wp--custom--color--gray--500)"
  			}
  		]
  	}
  }
}

I added the custom variables for the actual colors themselves and then plugged those into the color palette that comes with Ollie so the names/slugs are the same, but they’re using my custom colors. I specifically did it this way because I didn’t want to add all of those custom colors into the color palette (which could get pretty noisy) but still have the option to reference them in my own code later.

The color palette names/slugs are named semantically, meaning that the name means something beyond the specific color set. For instance, the name “Brand” doesn’t refer to a specific color like blue, red, green, etc but instead refers to how that color should be used.

So by keeping those semantic names for the Ollie theme the same, I can continue to use their pre-created patterns and all of the colors will still work. If I had named the color palette slugs something completely different, then all of the patterns that use the old slugs wouldn’t work correctly.

Template inheritance

Child themes have the ability to override templates in the parent theme. Hybrid (and classic) themes have used PHP template inheritance for years and block themes also support overriding PHP templates (if there are any) in addition to overriding the newer HTML templates.

For block themes, templates should be placed in a /templates folder at the root of your child theme like so:

- my-child-theme/
  - templates/
    - index.html

There is a great lesson article on Full Site Editing that breaks down the way template inheritance works in block themes very succinctly and I’d recommend you give that a read (and thanks Carolina for such a great resource)!

Here’s a quick breakdown for both block and hybrid themes:

Block Themes

Checks the /templates folder for HTML overrides first, then PHP. HTML templates take priority over equivalent PHP templates

Hybrid (and Classic) Themes

Looks for PHP templates in the same location as the parent theme in the child theme and uses them if present.

You can read more about WordPress template hierarchy here.

Conclusion

You’re now empowered to create your own child theme for a block theme or a hybrid theme! Taking this extra step can help you ensure your changes don’t get lost either to theme updates or accidents in the dashboard.

Happy coding!

Further Reading



Leave a Reply

Your email address will not be published. Required fields are marked *