Skip to content
Websites

Astra child theme: complete guide to a custom theme

Maciej Rostocki 9 min read Updated 2026-05-12
Astra child theme: complete guide to a custom theme

A child theme has been WordPress standard practice since 2009, but many developers still either edit the parent theme directly or use plugins like Code Snippets to manage CSS. Both approaches are problematic: the first falls apart on updates, the second blurs responsibility for code between the database and files. This text shows step by step how to build a solid Astra child theme that survives updates and lets you scale a client’s project for years.

Astra Pro has been our default since 2018, and child theme architecture has become a central element of every B2B project. The convention we describe here has been iterated through 20+ production deploys (DACH plus Poland) and is published as a reference for other developers and for technical clients who want to understand “what exactly they are buying”. Stack: Astra Pro 4.x plus WordPress 6.7 plus Gutenberg. For context we also recommend our text on Gutenberg vs Elementor.

Why a child theme and not editing Astra Pro directly

Astra Pro receives updates every 2-4 weeks. Updates are important because they deliver security patches, compatibility with the latest WordPress, and new features (e.g. recently Container Query support in 4.x). If you edit parent theme files directly, every update overwrites your changes and the client suddenly sees a “broken” site after an automatic update. Fixing it takes 2-4 hours and is a recurring cost on every update.

A child theme solves this problem structurally. The parent theme (Astra Pro) provides the framework, receives updates, and its files are not modified. The child theme contains only your brand-specific changes: color palette, fonts, layout overrides, custom blocks, plugin integrations. An Astra Pro update does not affect the child theme because WordPress loads child theme files with higher priority.

The second argument for a child theme: separation of concerns. Parent theme = framework decisions, child theme = brand decisions. This separation is valuable for the project team: design system, fonts, colors are in one place (child theme); a rebrand boils down to editing 3-5 files, not hunting for hardcoded styles across 50 parent theme files.

Setup: five files that make a child theme

A minimal working child theme requires 5 files. Below is a copyable template we use for every new client.

File 1: style.css. This file MUST have WordPress child theme metadata in the header, including the key Template: astra. Without this directive, WordPress will not recognize the folder as a child theme.

/*
Theme Name: My Custom Astra Child
Description: Custom child theme based on Astra Pro
Template: astra
Version: 1.0.0
Text Domain: my-child
*/

File 2: functions.php. Here we enqueue parent theme styles plus child styles, load textdomain for multilingual, register custom block patterns, add hooks. The core element is enqueueing parent + child stylesheet with a dependency chain and filemtime cache busting (covered in more depth in the enqueue best practices section).

File 3: screenshot.png. Image 1200×900 pixels, visible in wp-admin under Appearance plus Themes. This is “branding” for the content editor; it has no impact on the front-end. We typically use a composition with the client logo plus dominant brand color as preview.

File 4: assets/css/main.css. Compiled brand styles. In practice we write SCSS in local dev, compile to main.css before deploy. Here go design tokens (CSS custom properties), grid system overrides, typography rules, button styles, custom block styles. This is the central branding file.

File 5: assets/js/main.js. Interactive components, if needed (e.g. animated transitions, custom lazy-load, scroll-triggered headers). For most B2B projects this file is empty or minimal (1-3 KB). Performance value: do not load 200 KB jQuery if you only use a single scroll listener.

After creating the 5 files, we package them into a ZIP and install in wp-admin under Appearance plus Themes plus Add New plus Upload Theme. Activate. The front-end should look like parent Astra (clean default), because child styles are still empty. From this moment all modifications go only to the child theme.

Overriding parent template files

Any PHP file that exists in the parent theme can be overridden by creating a file of the same name in the child theme. WordPress automatically picks the version from the child theme. The most frequent files we override: header.php, footer.php, single.php (for a custom blog article layout), archive.php (for a custom listing), 404.php (for a branded error page).

The WordPress template file hierarchy is well documented at developer.wordpress.org. In short: for a single post WordPress first looks for single-{post_type}.php, then single.php, then singular.php, then index.php. Each of these files can be overridden in the child theme.

Astra has an additional layer: a template-parts folder with header and footer modules (Header Builder, Footer Builder). For a custom header layout we copy template-parts/header/header.php from parent to child and modify. For a custom footer, analogously.

When to override and when to use Astra Customizer: if the change is one-off and simple (color, font, padding), use Customizer. If the change is structural (whole header layout, custom widget area, branded 404 page), override in the child theme. Our rule: override when the modification exceeds 3 lines of CSS or requires PHP logic.

Custom Gutenberg blocks in the child theme

Custom Gutenberg blocks are the strongest productivity lever for the client’s content editor. Instead of writing HTML in “Custom HTML block” or gluing 5 generic blocks together, the editor picks “Case Study Tile” from the menu and fills in 4 fields: title, client, metric, link.

Two approaches to custom blocks. First: theme.json plus block patterns (PHP-side rendering, static block patterns registered in functions.php). Easier, faster, but limited to simple templates without interactivity. Second: full custom block (JS-side rendering, React component, build pipeline with webpack or @wordpress/scripts). Heavier, but fully flexible.

For 80% of B2B client cases, block patterns are enough. Example: register a block pattern for “Pricing tile” with 3 columns, each containing a heading plus list plus button. The editor picks the pattern from the menu, modifies content, layout stays fixed. Real example from our portfolio: our own site has 6 custom block patterns for typical package page layouts.

theme.json is a configuration file from WordPress 5.9+ that declares design tokens (color palette, typography presets, spacing, border styles) available to the content editor in Gutenberg. There you list the client’s custom color palette (5-7 named colors: primary, secondary, accent, surface, ink, bg-1, bg-2), typography (heading font, body font, scale), spacing system. The content editor sees these tokens in the Gutenberg UI and cannot pick an “off-brand” color.

Enqueue scripts and styles: best practices

Child theme performance starts with correct enqueue. Four best practices we apply in every project.

First: dependency chain. Parent style (astra-style) must be listed as a dependency for child style. WordPress then loads parent before child, and child can override parent rules. Without this dependency, the CSS cascade may behave “randomly” depending on file mtime.

Second: cache busting via filemtime. The file version (the version parameter in wp_enqueue_style) should be filemtime() of the file. After deploy, mtime changes and the browser cache invalidates automatically. Without cache busting, clients see the old CSS version after deploy until they hard refresh.

Third: lazy-load JS for non-critical scripts. Scripts not needed above-the-fold are loaded with the defer or async attribute. WP hooks let you delay JS loading selectively for specific handles. Benefit: the HTML parser does not block waiting for JS, LCP improves by 200-500 milliseconds typically.

Fourth: conditional enqueue. A script for the product gallery on a single product page should not load on the home page. We check is_product() or get_post_type() before enqueue. Real impact from our portfolio: home page JS payload dropped from 45 KB to 18 KB after introducing conditional enqueue for individual context-specific scripts.

Multilingual support: Polylang plus child theme

Multilingual is a standard for B2B clients in DACH and EU. The child theme must be prepared for translations from setup, even if the client starts with one language (adding later is 2-3x more expensive than day-1).

Translation-ready strings we use with functions __() or esc_html__() with the child theme text domain. Every string visible to the user must pass through the translation function with the text domain, otherwise Polylang will not capture it for translation.

The .pot file we generate with WP-CLI: wp i18n make-pot. The .pot file contains all strings from the child theme, ready for translation. A translator fills out .po per language (e.g. en.po, de.po), we compile to .mo and load through load_child_theme_textdomain in functions.php.

Polylang synchronizes post translations: when the editor creates the Polish version of a post, Polylang offers a “create EN translation” button. All meta plus content blocks are copied structurally, the editor only translates texts. For Gutenberg blocks this sync works great; for Elementor data structures it often requires manual fixing (another argument for Gutenberg, described in our comparison text).

Deploy workflow: local, staging, production

The child theme should not be edited on production. Our standard: local dev (Local by Flywheel or wp-env), staging (client subdomain on the same host), production (client main domain). Every change passes through all three environments.

Git workflow: the whole child theme folder (theme/) is in a Git repo, separate for each client. We ignore node_modules, .DS_Store, vendor folders. Branch strategy: main for production, develop for staging, feature branches for single tasks (e.g. feature/add-pricing-block). Every commit has a clear message following Conventional Commits.

Deploy from local dev to staging is via SFTP/rsync. Our standard pipeline: PHP syntax check, scp child theme to staging, LSCache purge (LiteSpeed Cache 7.8.1 active on Cyber Folks plus Hetzner hosting), lswsctrl restart (CyberPanel only), smoke test of 3 pages. The whole process is 5-10 minutes from local PC.

Production deploy from staging is analogous, but we add a backup before deploy (wp db export plus tar -czf folder uploads) and full Lighthouse audit after deploy. The backup is packaged with a timestamp filename and stored for 30 days. We document the workflow in internal SOPs as a reference for other clients. After deploy we email the client a change report. Contact us: contact page.

FAQ

Can I use a child theme without Astra Pro (only free Astra)?

Yes, child theme architecture works with Astra Free. You lose some Astra Pro hooks (Header Builder Pro, Footer Builder Pro, Custom Layouts, WooCommerce Pro integrations). For B2B projects, Astra Pro is a necessity; for a personal portfolio, Astra Free is enough. Astra Pro price: 60 USD/yr for single site, 240 USD/yr agency (unlimited sites).

After an Astra Pro update: will the child theme break?

No, if the child theme is well-built. The most common problem: the child theme relies on a parent theme function that has been removed or changed signature in the new version. Solution: always wrap with function_exists() and is_callable() check before calling a parent theme function. The second common problem: CSS specificity wars when the parent theme added a new rule with higher specificity. Solution: regular check of staging after each Astra update, before promoting to prod.

Can the child theme be transferred from site to site?

Yes, ZIP install like any theme. But: cleanup of site-specific functions. If the child theme has a hardcoded client URL, hardcoded API keys, or site-specific custom post types, these elements need to be extracted before deploying to another site. Our standard: branding variables in one config file (e.g. inc/config.php), the rest neutral and portable.

Can I use a child theme with Elementor?

Technically yes, a child theme is abstractly compatible with any page builder. We do not recommend this combination (see Gutenberg vs Elementor). With Elementor Pro the child theme loses most of its value, because the layout is edited in Elementor, not in PHP child theme. Custom Gutenberg blocks in the child theme are orthogonal to Elementor widgets.

How long does it take to build a child theme from scratch?

Realistically 40-80 hours of dev work for a solid B2B client child theme. Breakdown: 4-8 hours setup plus baseline (5 files plus enqueue plus textdomain), 16-24 hours styling plus design tokens, 12-20 hours custom block patterns, 4-8 hours multilingual setup, 4-8 hours testing plus deploy. Price: 6,000-12,000 PLN in our quote. More on factors influencing the price: WordPress site pricing.

§ From the studio

A new article every month, zero spam.

One case study or technical deep-dive. No clickbaits, no „10 reasons". Unsubscribe with one click.

— Related articles
Websites

Cloudflare + WordPress: setup + cache strategy

2026-03-16 · 10 min read
Websites

Multilingual WordPress: Polylang vs WPML in 2026

2026-03-09 · 11 min read
Websites

Elementor to Gutenberg migration: runbook

2026-03-02 · 9 min read
Back to all posts
Scroll to Top