If you’re a WordPress developer tired of bloated themes and messy PHP templates, it’s time to upgrade your workflow. By using Advanced Custom Fields (ACF) and Timber (Twig templating for WordPress), you can build clean, structured, and scalable themes β the way modern development should be.
In this overview guide, you’ll learn how to build a custom WordPress theme from scratch using ACF and Timber, with a focus on modularity, speed, and maintainability.
π§° What Youβll Need
Before you start, make sure you have:
- A local WordPress installation (e.g., using LocalWP, MAMP, XAMPP)
- Composer installed on your machine
- A basic understanding of PHP, WordPress theme structure, and the ACF plugin
Step 1: Set Up the Theme Folder
- Go to
wp-content/themes/ - Create a new folder for your theme:
/wp-content/themes/my-custom-theme - Add basic theme files:
style.css index.php functions.php - In
style.css, add your theme header:/* Theme Name: My Custom Timber Theme Author: You Version: 1.0 */
Step 2: Install Timber via Composer
Timber is a plugin but can (and should) be used via Composer.
- In your theme directory, initialize Composer:
composer init - Require Timber:
composer require timber/timber - In
functions.php, load Composer autoload:require_once __DIR__ . '/vendor/autoload.php';
Step 3: Enable Timber and Set Theme Paths
In functions.php, initialize Timber and configure it:
use Timber\Timber;
$timber = new Timber();
Timber::$dirname = ['templates', 'views']; // Create these folders
add_theme_support('timber');
add_theme_support('post-thumbnails');
Step 4: Create the Theme Structure
Now set up your folder and file structure like this:
my-custom-theme/
βββ functions.php
βββ style.css
βββ index.php
βββ vendor/
βββ templates/
β βββ base.twig
β βββ index.twig
β βββ partials/
βββ views/
Step 5: Create the Base Twig Template
Create templates/base.twig:
<!DOCTYPE html>
<html {{ site.language_attributes }}>
<head>
<meta charset="{{ site.charset }}">
<title>{{ site.name }} - {{ title }}</title>
{{ function('wp_head') }}
</head>
<body>
<header>
<h1><a href="{{ site.url }}">{{ site.name }}</a></h1>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© {{ now|date("Y") }} {{ site.name }}</p>
</footer>
{{ function('wp_footer') }}
</body>
</html>
Step 6: Create the Index Template
Create templates/index.twig:
{% extends "base.twig" %}
{% block content %}
<h2>Latest Posts</h2>
{% for post in posts %}
<article>
<h3><a href="{{ post.link }}">{{ post.title }}</a></h3>
<p>{{ post.preview.excerpt(40) }}</p>
</article>
{% endfor %}
{% endblock %}
And in index.php, render the Twig file:
$context = Timber::context();
$context['posts'] = Timber::get_posts();
Timber::render('index.twig', $context);
Step 7: Add ACF for Custom Fields
- Install the ACF plugin (Free or Pro).
- Create a Field Group in the WP Admin (e.g., “Page Options”).
- Add fields like:
- Page Header (Text)
- Featured Image (Image)
- CTA Button (Link)
Step 8: Access ACF Fields in Twig
In your page controller (page.php), pass ACF fields to Timber:
$context = Timber::context();
$context['post'] = Timber::get_post();
$context['header'] = get_field('page_header');
$context['cta_button'] = get_field('cta_button');
Timber::render('page.twig', $context);
Then in templates/page.twig:
{% extends "base.twig" %}
{% block content %}
<h1>{{ header }}</h1>
<p>{{ post.content }}</p>
{% if cta_button %}
<a href="{{ cta_button.url }}">{{ cta_button.title }}</a>
{% endif %}
{% endblock %}
Step 9: Add Partial Templates
To keep things modular, split your layout into partials:
templates/partials/header.twigtemplates/partials/footer.twigtemplates/partials/post-card.twig
Then include them like this:
{% include 'partials/post-card.twig' with { post: post } %}
This makes your theme DRY (Donβt Repeat Yourself), maintainable, and scalable.
Step 10: Final Touches
- Enqueue scripts and styles properly using
wp_enqueue_scripts. - Add menus using
register_nav_menus()and display them in Twig. - Use custom post types and taxonomies for advanced use cases.
β Why Use ACF + Timber?
πΉ Better Code Separation
Twig templates separate logic from presentation β no messy PHP in your HTML.
πΉ Faster Development
Reusable partials, cleaner syntax, and easier debugging = faster builds.
πΉ Clean, Maintainable Themes
ACF gives you full control over content structure. Timber keeps your theme files clean and readable.
πΉ Scalability
Great for larger, more complex projects where structure matters.
Final Thoughts
Building a custom WordPress theme from scratch with ACF and Timber elevates your workflow to a professional level. Youβll write cleaner code, develop faster, and create more scalable themes that clients (and future you) will thank you for.
Whether you’re freelancing or working on large-scale sites, using this modern stack is a game-changer.