Tailwind CSS

Add Tailwind CSS to a NukeJS project. Because NukeJS controls the HTML pipeline you run Tailwind as a CLI watcher alongside the dev server.

⚠️
No plugins

NukeJS intentionally avoids using magic plugins to add features. It keeps everything minimal and simple so it’s easy for everyone to understand.

Integration

Install

terminalbash
npm install -D tailwindcss @tailwindcss/cli

Create the CSS entry file

Tailwind v4 uses a CSS-first approach. Create a single file at the project root that imports the framework and adds any global base styles:

global.csscss
@import "tailwindcss";

body {
    @apply bg-blue-500;
}

The Tailwind CLI reads this file, scans your TSX for class names, and writes the compiled output to app/public/styles.css. Never edit styles.css by hand — it is regenerated on every build.

Start the watcher with middleware

NukeJS loads middleware.ts before every request. Spawn the Tailwind CLI watcher there in development so it runs inside the same nuke dev process — no second terminal required:

middleware.tstypescript
import { spawn } from 'child_process'
import type { IncomingMessage, ServerResponse } from 'http'

if (process.env.ENVIRONMENT !== 'production') {
    spawn(
        'npx',
        ['@tailwindcss/cli', '-i', './global.css', '-o', './app/public/styles.css', '--watch'],
        { stdio: 'inherit', shell: true }
    )
}

export default async function middleware(req: IncomingMessage, res: ServerResponse) {
    // your existing middleware logic
}

The ENVIRONMENT !== 'production' guard ensures the watcher only runs locally. In production the CSS is compiled by the build script before NukeJS bundles the app.

Update package.json

Add a build script that compiles Tailwind first, then hands off to NukeJS:

package.jsontypescript
{
    "scripts": {
        "dev": "nuke dev",
        "build": "npx @tailwindcss/cli -i ./global.css -o ./app/public/styles.css && nuke build"
    },
    "devDependencies": {
        "@tailwindcss/cli": "^4.2.1",
        "tailwindcss": "^4.2.1"
    }
}

Load the stylesheet in your layout

NukeJS serves everything in app/public/ as static files, so /styles.css resolves directly to the compiled output. Inject it into every page via the root layout:

app/pages/layout.tsxtypescript
import { useHtml } from "nukejs"
import React from "react"

export default function Layout({ children }: { children: React.ReactNode }) {
    useHtml({
        link: [{ rel: 'stylesheet', href: '/styles.css' }],
    })
    return <>{children}</>
}

Use Tailwind classes

With the layout in place, use any Tailwind utility class in your page components:

app/pages/index.tsxtypescript
import { useHtml } from "nukejs"

export default function Index() {
    useHtml({
        bodyAttrs: {
            style: "margin:0;height:100vh;display:flex;justify-content:center;align-items:center;"
        }
    })

    return (
        <div className="flex flex-col items-center gap-4 text-center px-6">
            <img src="/nuke.png" alt="NukeJS logo" className="w-24 h-24" />
            <h2 className="text-3xl font-bold tracking-tight text-white">
                Welcome to NukeJS
            </h2>
            <p className="text-slate-300 max-w-sm text-sm leading-relaxed">
                React. Weaponized. — now with utility-first styling.
            </p>
            <a
                href="/docs"
                className="mt-2 px-5 py-2.5 rounded-lg bg-indigo-600 hover:bg-indigo-500 transition-colors text-sm font-medium text-white"
            >
                Read the docs →
            </a>
        </div>
    )
}

Tailwind with a client component

Client components use Tailwind classes exactly the same way — as long as the CSS is loaded in the layout, it applies everywhere:

app/components/ThemeToggle.tsxtypescript
"use client"
import { useState } from 'react'

export default function ThemeToggle() {
    const [dark, setDark] = useState(true)

    return (
        <button
            onClick={() => setDark(d => !d)}
            className="flex items-center gap-2 px-4 py-2 rounded-lg border border-slate-700 bg-slate-800 text-sm text-slate-200 hover:border-indigo-500 transition-colors"
        >
            <span>{dark ? '🌙' : '☀️'}</span>
            {dark ? 'Dark mode' : 'Light mode'}
        </button>
    )
}
Use Tailwind v4 — it's config-free

Tailwind v4 eliminates tailwind.config.js entirely. Theme tokens, custom utilities, and source paths all live in your CSS entry file via @theme, @utility, and @source directives.