How to Build a WordPress Theme Without a Template

Most WordPress tutorials assume you’ll start from Underscores, a boilerplate, or a page builder. But when a client hands you a Figma file or a single JPEG mockup with a layout that no starter theme can replicate, that assumption falls apart. Building a WordPress theme from scratch means scaffolding every core file yourself – style.css, functions.php, header.php, footer.php, theme.json – then converting the design into template parts and dynamic content by hand.
I’ve run this exact sequence across dozens of client conversions, moving from a flat design file to a fully editable WordPress theme with no boilerplate underneath. Here’s that process laid out the way I actually build it.
What Building a Theme Without a Starter Template Actually Means
Building without a starter template means you create every file in the theme folder intentionally, named by you, and scoped to exactly what the design requires. You’re not cloning Underscores, Astra, or any other starter theme. You’re not assembling the front end inside a page builder plugin. Nothing ships that you didn’t put there on purpose.
The word "custom" gets thrown around loosely. A theme built by heavily modifying Underscores is still, structurally, a starter-theme build. A theme assembled with a visual page builder is a builder build, even if it looks custom to the client. This article covers neither. For the fuller definition of what separates a genuinely custom theme from a modified template, see my detailed breakdown of what a custom WordPress theme actually is.
From-Scratch, Child Theme, or Page Builder: What’s the Difference?
These three approaches get confused constantly. Here’s the breakdown:
- From-scratch: You write style.css, functions.php, header.php, footer.php, and any template parts yourself, with zero inherited code from another theme.
- Child theme: You install a parent theme and override specific files or styles in a child folder. The parent theme’s structure, hooks, and defaults still govern most of the site.
- Page builder: You install a plugin like a visual drag-and-drop editor on top of any theme and build pages through that plugin’s interface instead of writing PHP templates.
A child theme makes sense when you like 80% of an existing theme and only need to adjust specific pieces. A from-scratch build works when the design has no reasonable starter theme match, when animation or interaction requirements are unusual, or when a client needs full long-term control over the codebase without inherited bloat.
When Building Without a Starter Theme Actually Makes Sense
I skip starter themes when at least two of these conditions are true: the design has a highly custom layout system that doesn’t map to typical page templates, the client wants zero unused CSS or JS shipping in the theme, there’s a long-term maintenance relationship where a lean codebase pays off, or the project includes custom animation work like the kind covered in my guide to building an animated navigation bar in WordPress. If none of those apply, a starter theme or child theme is usually the faster, safer choice. See my comparison of custom WordPress themes vs. templates before committing to a from-scratch build.
Building a WordPress theme from scratch means manually creating every required theme file and converting a design file into template parts and dynamic content, with no starter theme or page builder underneath.
Step 1: Break the Design Into Reusable Sections
Before opening a code editor, break the design into named, repeatable sections. This step determines how clean your template-part structure ends up being. Almost everyone rushes it.
If you’re starting from a Figma file, my detailed Figma to WordPress walkthrough covers the design-tool side. If you’re starting from a flat JPEG mockup, see my guide to converting a JPEG design into a WordPress theme.
Identifying Repeatable Blocks
Go through the design and mark every section that either repeats on the page or will repeat across other pages. Typical candidates:
- Header and primary navigation
- Hero or banner section
- Feature or service cards (usually a repeating grid)
- Testimonial or review blocks
- Call-to-action bands
- Footer with columns and legal links
Anything that appears more than once, or that a client will want to reuse on a future page, becomes a template part. A truly one-off landing page section can stay inside a single page template instead.
Naming Before You Write Code
I name every section before writing a line of PHP, using the same names across the design file, the folder structure, and the function names in functions.php. If the hero section is hero-primary in my notes, the template part file is template-parts/hero-primary.php, and any hook or function tied to it uses hero_primary in its name. Consistent naming keeps a six-week client build maintainable in month eight.
Design Breakdown Checklist:
- Every repeating section is identified and named
- One-off sections are flagged separately from reusable ones
- Section names are consistent across design notes, file names, and function names
- Dynamic areas (anything a client will edit) are marked apart from static content
- Responsive behavior is noted per section
Step 2: Set Up the Theme File Structure
With sections named, scaffold the actual theme folder. According to the WordPress Theme Handbook, a theme technically needs only a style.css file and an index.php file. Almost every real build needs considerably more.
The Core Files: style.css, functions.php, theme.json
- style.css holds the theme’s metadata (name, author, version) in a comment header at the top, and can also hold your actual CSS if you’re not using a separate build pipeline.
- functions.php registers theme supports, enqueues scripts and stylesheets, registers custom field hooks, and defines any custom PHP logic the theme needs.
- theme.json controls global settings and styles for block-based themes and block-editor behavior, including color palettes, typography scales, and spacing units.
- index.php is the fallback template WordPress uses when no more specific template matches a request.
header.php and footer.php
header.php outputs everything from the opening <html> tag through the navigation and any sticky header markup. It’s called at the top of most page templates using get_header(). footer.php mirrors that at the bottom, closing out markup and calling wp_footer(), which most plugins rely on to inject scripts. Get the placement of wp_head() and wp_footer() right early – plugins and analytics scripts assume they exist exactly where WordPress expects them.
A Folder Structure That Works
I use this same structure across every client conversion:
my-custom-theme/
├── style.css
├── functions.php
├── index.php
├── header.php
├── footer.php
├── theme.json
├── template-parts/
│ ├── header-nav.php
│ ├── hero-primary.php
│ ├── card-grid.php
│ ├── cta-band.php
│ └── footer-columns.php
├── templates/
│ ├── front-page.php
│ ├── page.php
│ └── single.php
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
└── inc/
├── enqueue.php
├── custom-fields.php
└── theme-setup.php
I keep inc/ for anything that would otherwise bloat functions.php into an unreadable file. I never let functions.php grow past a few hundred lines before splitting logic into inc/.
| File / Folder | Purpose |
|---|---|
| style.css | Theme metadata (required) plus base CSS |
| functions.php | Enqueue scripts/styles, register supports, load inc/ files |
| index.php | Fallback template (required) |
| header.php | Opening markup, nav, wp_head() |
| footer.php | Closing markup, wp_footer() |
| theme.json | Block editor global styles and settings |
| template-parts/ | Reusable section templates (hero, cards, CTA, etc.) |
| templates/ | Page-type-specific templates (front page, single post) |
| inc/ | Split-out PHP logic (custom fields, setup, enqueue) |
| assets/ | Compiled CSS, JS, and image files |
For a full build using this structure, see my longer walkthrough on building a WordPress theme from scratch.
Step 3: Build the Stylesheet and Header/Footer Templates
With the folder in place, write style.css and the header/footer templates before touching any template part. This gives you a visible, loading page almost immediately, which matters for client check-ins and your own sanity.
Writing style.css With Theme Metadata
The comment block at the top of style.css is not optional. WordPress reads it to register the theme in the admin dashboard. At minimum, you need a Theme Name field:
/*
Theme Name: Client Project Custom Theme
Author: Your Name or Agency
Version: 1.0
Text Domain: client-project
*/
Everything below that comment block can be your actual CSS, or you can enqueue a compiled stylesheet from assets/css/ and keep style.css mostly metadata.
Coding Header and Footer From the Design
Build header.php and footer.php directly from the design file’s top and bottom sections, matching spacing, breakpoints, and navigation behavior before building anything in between. This forces you to solve navigation responsiveness (mobile menu toggles, sticky behavior) early, rather than discovering a layout conflict after ten template parts already depend on the header’s height.
Step 4: Convert Static Sections Into Template Parts
This is where the design breakdown from Step 1 becomes actual reusable PHP files.
What Template Parts Do
A template part is a self-contained chunk of markup, loaded into a parent template with get_template_part(), that you can reuse across multiple pages without duplicating code. Change the card-grid template part once, and every page using it updates. Skip this step and you’ll end up hardcoding the same markup into five different page templates, turning a five-minute style change into a five-file hunt.
Building Reusable Parts From Repeated Design Blocks
For each section you named in Step 1, create a matching file in template-parts/ and load it from the relevant page template with get_template_part( 'template-parts/hero-primary' ). Build these in the order they appear on the page, top to bottom, testing each one in the browser before moving to the next. That order also matches how most clients review progress.
Step 5: Wire Up Dynamic Content and Custom Fields
Static markup is only half the job. A real theme lets a client or editor change text, images, and repeatable content without touching code.
Using Custom Fields for Client-Editable Content
Custom fields pull client-editable values – a hero headline, a button link, a testimonial quote – into your template parts instead of hardcoding them. Anywhere the design has content a non-developer will need to update, that content needs a field, not a hardcoded string.
Advanced Custom Fields for Structured Content
I use Advanced Custom Fields on almost every from-scratch build. It lets you define field groups (text, image, repeater, flexible content) and pull them into template parts with simple function calls, without building a custom admin interface from scratch. According to Advanced Custom Fields’ documentation, the plugin supports repeaters and flexible content fields, which cover most "list of cards" or "stack of testimonials" patterns. For simple sites, WordPress custom fields alone may suffice. For anything with repeating, structured content, ACF’s repeater and flexible content fields save real build time.
If your project needs editor-level layout flexibility rather than fixed template parts, that’s a distinct workflow from the file-based approach here. I cover it separately in how to design in the WordPress block editor and block editor vs. page builder plugins.
Step 6: Test Across Pages, Breakpoints, and Content States
A theme that looks right only in your one test browser, on your one test page, with placeholder content, is not finished. This step catches the failures that show up after launch.
Breakpoint Testing
Test every template part at a minimum of four widths:
| Breakpoint | Typical Width | What I Check |
|---|---|---|
| Mobile | 375px | Nav collapse, stacked cards, text wrapping |
| Large mobile / small tablet | 600px | Grid reflow, image scaling |
| Tablet | 768px–1024px | Two-column layouts, nav behavior |
| Desktop | 1280px+ | Max-width containers, whitespace balance |
Testing With Real-World Content
Design mockups show ideal content: short headlines, perfectly cropped images, testimonials that fit in three lines. Real client content rarely cooperates. Before launch, test each dynamic section with:
- An empty field (what happens if the client deletes the value?)
- A headline twice as long as the mockup shows
- An image with the wrong aspect ratio
- Zero repeater items and ten repeater items
- A very long single word (portfolio URLs, long client names) to check for overflow
Skipping this step is the single most common reason a theme looks perfect in the demo and breaks the week after launch.
Common Mistakes When Skipping a Starter Template
Skipping the file-structure planning stage. Developers who jump straight into functions.php without planning the folder structure end up with monolithic files that are painful to hand off. If another developer can’t find where the hero section lives in under thirty seconds, the structure failed.
Hardcoding content instead of using template parts. I’ve inherited client themes where every "card" on the homepage was a separate hardcoded block of HTML instead of a loop pulling from a single template part. The client couldn’t add a fourth service without calling a developer.
Not testing real content before launch. The gap between demo content and real content is where most post-launch bug reports come from. A headline that wraps to three lines instead of one, an image that’s portrait instead of landscape, a repeater field with zero items – these are all things Step 6 catches before a client ever sees them.
Starter Template vs. From-Scratch: A Quick Comparison
| Factor | Starter Template (e.g., Underscores) | From-Scratch Build |
|---|---|---|
| Setup time | Faster initial setup, inherited boilerplate | Slower start, every file built intentionally |
| Design flexibility | Constrained by starter’s existing structure | Matches any design exactly, no compromise |
| Learning curve | Lower for beginners, docs widely available | Higher, requires solid PHP/file-structure knowledge |
| Codebase size | Often carries unused boilerplate CSS/JS | Lean, only what the design needs |
| Long-term maintenance | Easier if design stays close to starter patterns | Easier if needs are unusual or evolving |
| Best for | Standard layouts, tight timelines, junior teams | Highly custom designs, animation-heavy builds, long client relationships |
If you’re still deciding which approach fits your project, my custom WordPress themes vs. templates guide walks through the decision in more detail. What makes a WordPress theme truly customizable is worth reading if you’re unsure whether a modified starter theme could get you there instead.
Here’s a video walkthrough of building a theme from an empty folder:
FAQ
What does it mean to build a WordPress theme without a template?
It means creating every core theme file yourself – style.css, functions.php, header.php, footer.php, and theme.json – rather than starting from a pre-built starter theme or assembling pages inside a page builder plugin.
What’s the difference between a starter template and building from scratch?
A starter template gives you pre-written boilerplate files and structure that you modify. Building from scratch means writing every file’s structure and logic yourself, matched exactly to one design.
Do I need a child theme if I’m not using a starter template?
No. Child themes exist to modify an existing parent theme’s files. A from-scratch build has no parent theme, so the child theme model doesn’t apply.
What files are required for a basic WordPress theme to work?
WordPress requires a style.css file with a valid theme header comment and an index.php file, according to the WordPress Theme Handbook.
What does style.css actually control in a WordPress theme?
It holds the theme’s required metadata (name, version, author) in its comment header and can also contain the theme’s actual CSS rules if you’re not using a separate compiled stylesheet.
What is functions.php used for?
It registers theme supports, enqueues CSS and JavaScript, hooks in custom fields, and holds (or loads from inc/) any custom PHP logic the theme needs.
What’s the difference between header.php and footer.php?
header.php outputs the opening markup and navigation and is loaded with get_header(). footer.php closes the markup and calls wp_footer(), loaded with get_footer().
What is theme.json and do I need one?
theme.json defines global block-editor settings like color palettes, typography, and spacing for block-based themes. You need one if your build uses block templates or block-editor styling controls. A purely classic PHP-template theme can function without it.
What are template parts and why do they matter?
Template parts are reusable chunks of markup loaded with get_template_part(), so a repeated section like a card grid or footer only needs to be built and maintained once.
How do I turn a Figma design into a WordPress theme?
Break the Figma file into named, repeatable sections, export or measure spacing and typography values, then build each section as its own template part following the build order used for any from-scratch theme. My Figma to WordPress step-by-step guide covers the design-to-code specifics.
How do I turn a JPEG mockup into a WordPress theme?
Measure the mockup’s spacing, colors, and breakpoints directly from the image, break it into repeatable sections, then follow the same six-step build sequence. See my guide on converting a JPEG design into a WordPress theme for the measurement workflow.
How do I break a design into reusable sections before coding?
Identify anything that repeats on the page or across pages (headers, card grids, CTA bands, footers), name each section consistently, and separate one-off content from reusable content before writing any code.
What is Advanced Custom Fields (ACF) and when should I use it?
ACF is a plugin that lets you define custom field groups, including repeaters and flexible content fields, and pull that content into theme templates. Use it any time a design includes repeating, client-editable content like testimonials or service cards.
How do custom fields connect to theme templates?
You define the field in ACF or WordPress’s native custom fields, then call that field’s value inside the relevant template part using a function call, so the template pulls live content instead of hardcoded text.
How many breakpoints should I test a custom theme against?
Test a minimum of four widths: roughly 375px for mobile, 600px for large mobile/small tablet, 768–1024px for tablet, and 1280px and up for desktop.
What content states should I test besides the default design?
Empty fields, unusually long headlines or text, wrong-aspect-ratio images, zero-item and many-item repeater fields, and very long unbroken strings like URLs.
What are the most common mistakes when skipping a starter theme?
Skipping file-structure planning before coding, hardcoding repeated content instead of using template parts, and shipping without testing real (non-mockup) content.
Is it ever a bad idea to build a theme without a template?
Yes. If the design is close to a standard layout, if the timeline is tight, or if the team is less experienced with raw PHP theme development, a starter theme or child theme is usually faster and lower risk.
How long does it typically take to build a theme without a starter theme?
Timelines vary heavily by design complexity and dynamic content needs. It depends entirely on the specific project scope.
Can I still use the WordPress block editor with a custom-built theme?
Yes, though the specifics depend on whether you build classic PHP templates or full block templates with theme.json. That’s a distinct workflow covered in how to design in the WordPress block editor.
What’s the risk of building a WordPress theme completely from scratch?
The main risks are underestimating the file-structure planning stage, hardcoding content that should be dynamic, and skipping breakpoint or content-state testing before launch.
How is a custom theme build different from using a page builder plugin?
A custom theme build writes PHP templates and template parts directly. A page builder plugin assembles pages visually through its own interface, usually storing layout data separately from standard WordPress template files.
What should my theme’s file structure look like before I start coding?
At minimum, plan for style.css, functions.php, index.php, header.php, footer.php, a template-parts folder for reusable sections, and an assets folder for compiled CSS/JS/images.
How do I know if my design needs a fully custom theme versus a modified starter theme?
If the layout doesn’t map to standard page patterns, if there’s significant custom animation, or if the client needs a lean long-term codebase, a from-scratch build usually wins. My custom WordPress themes vs. templates guide walks through this decision with more detail.
Next Steps
If you’re weighing whether this build method fits your project, start with custom WordPress theme development for the service-level overview, or what a custom WordPress theme actually is for the foundational definition. Designers working from a Figma file should read the Figma to WordPress workflow case study before starting Step 1.
Once your theme is scaffolded and template parts are working, animation and interaction are usually the next layer clients ask for. My guides on building an animated navigation bar and adding Lottie animations to WordPress pick up exactly where this article leaves off. If you’d rather have someone handle this entire process for you, Ivory Miracle provides web design and UI/UX services that cover from-scratch theme builds using this same file structure and testing methodology.
Sources
My right hand does not know how much my left hand has given, and vice versa. Over the years, I have given to orphanages, street children and their families, the elderly, women seeking livelihood opportunities, widows, and people who are on the fringes of society. My logic is simple: I never want anyone to feel as though they have been forgotten by society. Sometimes it does not take much. People on the streets have actually smiled at me simply because I gave them a cookie. That smile can stay with you much longer than you expect. No one should feel forgotten. I’m a little like the city that raised me. My favorite food is beef pares, tenderly cooked beef simmered for several hours until the meat practically melts in your mouth. Its taste cannot easily be contained by a poverty of vocabulary when it is cooked just right, and the stew is simply the icing on the cake. In a strange way, it reminds me of the beautiful ways God has arranged my heart through my unique experiences in life. Manila itself feels like a melting pot, no pun intended, of food, beverages, languages, art, people, contradictions, and real life. I think I have become a little like the city that raised me. Read About Me.



Join the conversation
Good work gets better when people bring thoughtful questions, ideas, and perspective.