Skip to content

Instantly share code, notes, and snippets.

@btantlinger
Created August 9, 2025 00:05
Show Gist options
  • Save btantlinger/38eed46717cd592722da5451252f856a to your computer and use it in GitHub Desktop.
Save btantlinger/38eed46717cd592722da5451252f856a to your computer and use it in GitHub Desktop.
TailPress + DDEV + Vite Setup

TailPress + DDEV + Vite Setup

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).


Prerequisites

  • 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


Step 1: Initialize DDEV WordPress Project

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]

Step 2: Install TailPress Theme via Composer

ddev exec "mkdir -p wp-content/themes && composer create-project tailpress/tailpress my-theme --working-dir=wp-content/themes"

Step 3: Install Node Dependencies

ddev npm --prefix wp-content/themes/my-theme install

Step 4: Configure Vite (vite.config.mjs)

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(),

        ],
    }
});

Step 5: Expose Vite and Tailpress Ports in .ddev/config.yaml

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

Step 6: Enqueue Assets in functions.php

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();

Step 7: Running the Development Environment

Start WordPress site (if not running):

ddev start

Start Vite dev server with hot reload:

ddev npm --prefix wp-content/themes/my-theme run dev

Build production assets:

ddev npm --prefix wp-content/themes/my-theme run build

Notes and Tips

  • 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.


Troubleshooting

  • 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 and functions.php URLs match your actual DDEV domain and ports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment