This guide walks you through setting up the TailPress WordPress theme inside a DDEV environment with Vite for asset building and hot module replacement (HMR).
-
DDEV installed and configured
-
Composer installed (used inside DDEV container)
-
Node.js and npm installed (bundled in DDEV web container)
-
Basic familiarity with WordPress theme development
mkdir tailpress-wp && cd tailpress-wp
ddev config --project-type=wordpress
ddev start
ddev wp core download
ddev wp core install \
--url="$DDEV_PRIMARY_URL" \
--title="My WordPress Site" \
--admin_user=admin \
--admin_password=admin \
[email protected]
ddev exec "mkdir -p wp-content/themes && composer create-project tailpress/tailpress my-theme --working-dir=wp-content/themes"
ddev npm --prefix wp-content/themes/my-theme install
Create or overwrite wp-content/themes/my-theme/vite.config.mjs
with:
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig(({ command }) => {
const isBuild = command === 'build';
return {
base: isBuild ? '/wp-content/themes/tailpress/dist/' : '/',
server: {
host: "0.0.0.0",
port: 3000,
strictPort: true,
origin: `${process.env.DDEV_PRIMARY_URL.replace(/:\d+$/, "")}:3000`,
cors: {
origin: /https?:\/\/([A-Za-z0-9\-\.]+)?(\.ddev\.site)(?::\d+)?$/,
},
},
build: {
manifest: true,
outDir: 'dist',
rollupOptions: {
input: [
'resources/js/app.js',
'resources/css/app.css',
'resources/css/editor-style.css'
],
},
},
plugins: [
tailwindcss(),
],
}
});
Add or update this section in your project’s .ddev/config.yaml
:
web_extra_exposed_ports:
- name: vite
container_port: 3000
http_port: 5172
https_port: 5173
- name: tailpress
container_port: 3000
http_port: 3001
https_port: 3000
Restart DDEV to apply changes:
ddev restart
Replace the functions.php
file in wp-content/themes/my-theme
with:
<?php
if (is_file(__DIR__.'/vendor/autoload_packages.php')) {
require_once __DIR__.'/vendor/autoload_packages.php';
}
// Helper function to get current host with https and port 3000 for Vite
function get_vite_dev_server_url() {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
// Adjust port to 3000 (vite default)
// If your site runs on a different port or no port, adjust accordingly
$host = preg_replace('/:\d+$/', '', $host); // remove existing port
return "{$scheme}://{$host}:3000";
}
function tailpress(): TailPress\Framework\Theme
{
return TailPress\Framework\Theme::instance()
->assets(fn($manager) => $manager
->withCompiler(new TailPress\Framework\Assets\ViteCompiler(get_vite_dev_server_url(), null, true, true), fn($compiler) => $compiler
->registerAsset('resources/css/app.css')
->registerAsset('resources/js/app.js')
->editorStyleFile('resources/css/editor-style.css')
)
->enqueueAssets()
)
->features(fn($manager) => $manager->add(TailPress\Framework\Features\MenuOptions::class))
->menus(fn($manager) => $manager->add('primary', __( 'Primary Menu', 'tailpress')))
->themeSupport(fn($manager) => $manager->add([
'title-tag',
'custom-logo',
'post-thumbnails',
'align-wide',
'wp-block-styles',
'responsive-embeds',
'html5' => [
'search-form',
'comment-form',
'comment-list',
'gallery',
'caption',
]
]));
}
tailpress();
ddev start
ddev npm --prefix wp-content/themes/my-theme run dev
ddev npm --prefix wp-content/themes/my-theme run build
-
Hot reload: Vite’s HMR will automatically reload CSS and JS changes in your browser.
-
Environment variables: The Vite config dynamically uses
DDEV_PRIMARY_URL
from DDEV for correct URLs. -
Clearing cache: If hot reload doesn’t seem to work, clear browser cache or try incognito mode.
-
If HMR doesn’t work, verify port exposure and CORS config in
vite.config.mjs
. -
Check browser console/network for websocket or CORS errors.
-
Ensure your
vite.config.mjs
andfunctions.php
URLs match your actual DDEV domain and ports.