There's a specific kind of frustration that comes from opening up an old Drupal 7 theme folder, staring at it, and thinking: "surely I can just move this over." The CSS still looks fine. The layout still makes sense. Nothing about the design is wrong. And yet the moment you try to bring it into a current Drupal build, nothing lines up. Templates don't render. Variables come back empty. Things that used to just work now throw errors that don't even look like Drupal errors.

The instinct is to treat this as a file-format problem — rename some extensions, swap some syntax, done. That instinct is wrong, and it's worth understanding exactly why, because the answer explains a lot about how Drupal actually works today.

The Old Setup: Everything Lived in One Place

Back in Drupal 7, a theme file was a .tpl.php file, and that name told you everything. It was PHP. Real, unrestricted PHP, sitting directly next to your markup:

<div class="node">

<h2><?php print $title; ?></h2>  

<?php if ($display_submitted): ?>    

<span class="submitted"><?php print $submitted; ?></span>  

<?php endif; ?>  

<?php print $content; ?> 

</div>

For a lot of people who cut their teeth on Drupal 7, this felt natural. Need a quick date format, a string tweak, a one-off conditional? Just write the PHP right there. No middleman, no restrictions, no going somewhere else to fix a small thing.

The catch is that "no restrictions" cuts both ways. Since the template file could run any PHP, there was nothing structurally stopping a themer from querying the database mid-template, calling a random function, or shoving business logic somewhere it didn't belong. Drupal 7 had conventions that pointed people toward keeping logic in preprocess functions and keeping templates for display only — but conventions are suggestions, not walls. Over a few years, on a site that's changed hands a few times, those walls tend to get quietly ignored, and you end up with a theme where nobody's entirely sure what's calculating something versus just displaying it.

Then Drupal Rebuilt Its Foundation — Theming Just Came Along for the Ride

Here's the part that a lot of "convert your theme" tutorials skip: the switch to Twig wasn't really a theming decision at all. It was a side effect of something much bigger.

Starting with Drupal 8, the project rebuilt its core on top of Symfony — an existing, independently maintained PHP framework that handles things like routing requests, managing services, and dispatching events. Instead of Drupal maintaining its own homegrown versions of all that plumbing forever, it borrowed a foundation that a much larger PHP community already builds and tests.

That single decision changed the shape of everything downstream, theming included. Symfony comes with its own template engine — Twig — and once Drupal's core was sitting on Symfony's architecture, using Twig for output was the natural fit rather than a separate philosophical stance about PHP templates being bad. It solved a real problem the old system had, but the reason it happened when it did is architectural, not stylistic.

What Actually Changes With Twig

Twig looks similar to the old templates at a glance:

<div class="node">  

<h2>{{ title }}</h2>  

{% if display_submitted %}    

<span class="submitted">{{ submitted }}</span> 

{% endif %} 

 {{ content }} 

</div>

But under the surface, it's deliberately boxed in. You can't call arbitrary PHP functions from inside it. You can't run a database query from inside it. You get a limited vocabulary — printing values, loops, conditionals, filters, and only the functions Drupal has explicitly exposed to it. A template genuinely cannot sneak in logic anymore, because the tool it's written in doesn't have the vocabulary to do that.

That has a side benefit that's easy to overlook: templates that can't execute arbitrary code are much harder to weaponize if someone finds a way to inject content into one. Twig also escapes output automatically unless you deliberately tell it not to, which quietly closes off a class of cross-site-scripting bugs that old PHP templates left wide open by default.

Why "Just Convert the Syntax" Doesn't Work

This is where the real obstacle sits, and it's not about file extensions. Here's what actually has to change when a Drupal 7 theme moves forward:

  • Inline logic has to be relocated. Whatever a Drupal 7 template used to compute inline — a formatted date, a permission check, a dynamically built CSS class — now has to live somewhere else entirely: a preprocess function, a plugin, code that runs before Twig ever sees the data.
  • The page-building process itself changed. Drupal 7 assembled pages through a mix of theme functions and a fairly direct rendering path. Modern Drupal builds pages through render arrays — nested, structured data describing the page — before any template gets involved at all. A theme has to cooperate with that pipeline, not bypass it.
  • The same-named template doesn't mean the same variables. Even if node.tpl.php becomes node.html.twig in name, what data actually reaches that template, and under what names, has usually changed, because a different set of preprocessing steps built it.
  • Template selection logic isn't the same either. The system deciding which template file applies to which piece of content was reworked as part of the same overhaul.

None of that is a syntax problem. It's a "the system this theme was built to plug into no longer exists in the same form" problem. The theme wasn't broken by Twig — it was written for a machine that got replaced underneath it.

So What Does Moving a Theme Forward Actually Involve?

In practice, the visual design — the CSS, the markup structure, the layout choices — can usually survive close to intact, and it's worth carrying over as a reference point. What can't survive is the assumption that a template file is allowed to think for itself. Every piece of logic that used to sit inline in a .tpl.php file needs a new, proper home in a preprocess function or a plugin built against Drupal's current APIs — and that groundwork has to happen before writing a single line of Twig makes sense.

This is also, unsurprisingly, the part of any Drupal 7-to-modern-Drupal project that tends to eat the most time. The visual layer is the easy part. Untangling years of logic that quietly accumulated inside old templates — and giving it a proper place to live — is where the real audit work happens, and it's usually where a legacy site's hidden technical debt shows up.

The Short Version

Twig replacing PHP templates wasn't Drupal deciding PHP was outdated. It was the visible tip of a much larger decision to build Drupal on top of Symfony's architecture, with render arrays reshaping how pages get assembled in between. Once that's clear, the frustration of "why can't I just move my theme over" stops being a mystery — it's the expected result of a system that now actually enforces the separation between what a page displays and what decides how it gets displayed, instead of just hoping developers keep the two apart on their own.