Page builders exist for a reason — they let non-developers build pages fast. But a lot of client sites end up on Elementor or Divi even when a developer is already building the theme, and the result is often 40+ extra database queries per page just to render layout that plain PHP templates could handle directly.
When a hand-coded theme actually makes sense
If the client (or you) will never touch page layout after launch — the design is finished, content changes happen through the editor but the structure doesn’t — a lightweight custom theme is usually worth the extra build time. If the client needs to rearrange sections themselves regularly, a page builder or block theme is the more honest choice, and fighting that need with a rigid custom theme just creates support tickets later.
A minimal theme structure
my-theme/
├── style.css // theme header + minimal reset
├── functions.php
├── index.php
├── header.php
├── footer.php
├── single.php
├── page.php
└── template-parts/
└── content.php
Enqueueing assets properly
add_action('wp_enqueue_scripts', function() {
wp_enqueue_style('theme-style', get_stylesheet_uri(), [], filemtime(get_template_directory() . '/style.css'));
wp_enqueue_script('theme-script', get_template_directory_uri() . '/js/main.js', [], filemtime(get_template_directory() . '/js/main.js'), true);
});
Using filemtime() as the version number means the browser cache busts automatically on every deploy, without you having to remember to bump a version string manually.
What you give up
No drag-and-drop layout editing, no visual page builder for the client. If that’s actually needed post-launch, this isn’t the right approach — a block theme using core Gutenberg patterns splits the difference better, giving structure without the query overhead of a full third-party builder.