Skip to main content

Hot reload with FrankenPHP

· 3 min read
Stephan Hochdörfer
Head of IT Business Operations

While preparing the slides for my talk about FrankenPHP and DDEV at the API Platform Conference 2026, I discovered the hot reload feature of FrankenPHP.

In modern JavaScript tooling, Hot Module Replacement (HMR) is the standard. The frontend refreshes automatically when files change, simplifying development.

In traditional PHP, we didn't have this workflow, but with FrankenPHP's hot reloading, the server watches for filesystem changes and pushes Mercure updates to the browser. Depending on your setup, the DOM will be morphed (this needs Idiomorph to be available) or as fallback, the page gets reloaded.

Important: Hot reload is for development environments only and should not be enabled in production, as it's not secure and slows down the application.

How to enable Hot Reload (server side)

To enable the hot reload feature in FrankenPHP, you need to:

  1. Enable Mercure in your Caddyfile:
mercure {
anonymous
}
  1. Enable hot_reload in the php_server configuration section:
php_server {
hot_reload
}

By default, the server process watches all files with specific extensions ./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml} in the current working directory.

You can explicitly configure a file list by passing a list to the hot_reload configuration:

php_server {
hot_reload src/**/*{.php,.js} config/**/*.yaml
}

How to enable Hot Reload (client side)

To make the browser aware of hot reload, include the following snippet in your templates:

Generic PHP Application

<?php if (isset($_SERVER['FRANKENPHP_HOT_RELOAD'])): ?>
<meta name="frankenphp-hot-reload:url" content="<?=$_SERVER['FRANKENPHP_HOT_RELOAD']?>">
<script src="https://cdn.jsdelivr.net/npm/idiomorph/dist/idiomorph.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/frankenphp-hot-reload/+esm" type="module"></script>
<?php endif ?>

Symfony/Twig

{% if app.request.server.has('FRANKENPHP_HOT_RELOAD') %}
<meta name="frankenphp-hot-reload:url" content="{{ app.request.server.get('FRANKENPHP_HOT_RELOAD') }}">
<script src="https://cdn.jsdelivr.net/npm/idiomorph/dist/idiomorph.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/frankenphp-hot-reload/+esm" type="module"></script>
{% endif %}

Sylius

Add this to your templates/shop/javascripts.html.twig template file:

{% if app.request.server.has('FRANKENPHP_HOT_RELOAD') %}
<meta name="frankenphp-hot-reload:url" content="{{ app.request.server.get('FRANKENPHP_HOT_RELOAD') }}">
<script src="https://cdn.jsdelivr.net/npm/idiomorph/dist/idiomorph.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/frankenphp-hot-reload/+esm" type="module"></script>
{% endif %}
{{ encore_entry_script_tags('app-shop-entry', null, 'app.shop') }}

In conclusion, FrankenPHP's hot reload feature simplifies development by automatically refreshing the frontend when files change. Remember to only use this feature in development environments, as it's not secure for production.