Setting up Custom Tailwind CSS with Laravel and Filament can often feel like navigating a complex maze, especially when integrating different versions and build tools for a production environment. This playbook outlines a robust, production-ready strategy for integrating Tailwind CSS v3 with Laravel 13 and Filament v3, ensuring a clean, efficient, and maintainable setup. If you've struggled with conflicting Tailwind versions, unoptimized CSS, or unregistered Filament themes, this guide is your definitive solution.
Our goal is to move from a problematic mixed setup to a streamlined architecture with two distinct, optimized pipelines for our main application (e.g., a restaurant frontend) and the Filament admin panel. This ensures efficient Custom Tailwind CSS with Laravel and Filament integration across your project.
The Initial Hurdles: Why Our Tailwind Setup Was Flawed
Before diving into the solution, let's understand the common pitfalls encountered in the initial setup of our restaurant-app project. These issues highlight why a structured approach to Custom Tailwind CSS with Laravel and Filament is crucial for a smooth development experience.
Problem A: Restaurant Frontend Used Tailwind CDN
The resources/views/layouts/app.blade.php (and welcome.blade.php) initially included the Tailwind Play CDN:
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: { fontFamily: {...}, colors: { brand: {...} } } } }
</script>
Why this is wrong for production:
- Tailwind’s Play CDN is strictly for prototyping and development, not production.
- It offers no purging or minification of unused classes, leading to significantly bloated CSS files.
- Configuration happens at runtime in the browser, which is inefficient and slows down page loads.
- It bypasses Laravel’s efficient asset pipeline, losing out on Vite's build optimizations.
- It can create conflicts with a compiled Filament theme later on, leading to unexpected styling issues.
Problem B: Mixed Tailwind v3 + v4 (Broken Hybrid)
Our package.json had conflicting dependencies: both @tailwindcss/vite (Tailwind v4's Vite plugin) and tailwindcss ^3.4. Simultaneously, vite.config.js loaded the v4 Vite plugin, while app.css still used v3 @tailwind directives. Filament's theme, however, explicitly expects Tailwind v3.
Filament v3 is designed to work exclusively with Tailwind v3. Tailwind v4 should only be used with Filament v4 (when it is officially released).
Problem C: Filament Theme Files Unregistered
Custom Filament theme files existed at resources/css/filament/admin/theme.css and resources/css/filament/admin/tailwind.config.js, but the AdminPanelProvider did not call ->viteTheme(...). Consequently, Filament never loaded or applied our custom compiled theme, making it seem like your changes had no effect.
Problem D: Misunderstanding public/css/filament/
The public/css/filament/ directory is where Filament's default or published assets reside. It is not the official path for styling your main restaurant site, nor is it the correct location or method for loading a custom theme for the Filament admin panel. Attempting to use it this way leads to confusion and incorrect asset loading.
The Refined Architecture: Two Dedicated Pipelines for Custom Tailwind CSS
The core of our solution for Custom Tailwind CSS with Laravel and Filament is to establish two distinct, optimized asset pipelines. This ensures each part of the application (the main restaurant site and the Filament admin panel) has its own correctly configured Tailwind setup, avoiding conflicts and maximizing performance.
| Surface | Entry CSS | Tailwind config | Loaded by |
|---|---|---|---|
| Restaurant site | resources/css/app.css |
root tailwind.config.js
|
@vite([...]) in Blade |
| Filament /admin | resources/css/filament/admin/theme.css |
resources/css/filament/admin/tailwind.config.js |
->viteTheme(...) |
Crucially, do not attempt to merge these into one Tailwind configuration. Each pipeline has unique content scanning and preset requirements that make a combined configuration impractical and error-prone.
Step-by-Step Implementation: Fixing Your Custom Tailwind CSS Configuration
Let's walk through the exact changes needed to implement this dual-pipeline architecture for our Custom Tailwind CSS with Laravel and Filament setup. Follow these steps carefully to ensure a robust and maintainable environment.
Step 1: Pin npm to Tailwind v3 Only
First, ensure your package.json explicitly uses Tailwind v3 and removes any conflicting v4 dependencies. This is vital for Filament v3 compatibility.
File: package.json
Removed:
-
@tailwindcss/vite(This is the v4 plugin and should not be present)
Kept / Set:
{
"devDependencies": {
"@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.20",
"autoprefixer": "^10.4.21",
"laravel-vite-plugin": "^3.1",
"postcss": "^8.5.28",
"postcss-nesting": "^14.0.1",
"tailwindcss": "^3.4.19",
"vite": "^8.0.0"
},
"scripts": {
"build": "vite build",
"dev": "vite"
}
}
After modifying package.json, always run:
npm install
This command will update your node_modules to reflect the changes in package.json.
Step 2: Vite Configuration (Laravel Plugin Only)
Configure vite.config.js to use only the Laravel Vite plugin, specifying all necessary entry points for both the restaurant site and the Filament admin. This ensures Vite knows which CSS and JS files to compile.
File: vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/css/app.css',
'resources/js/app.js',
'resources/css/filament/admin/theme.css',
],
refresh: true,
}),
],
server: {
watch: {
ignored: ['**/storage/framework/views/**'],
},
},
});
What was removed vs the broken setup:
-
import tailwindcss from '@tailwindcss/vite'(v4 plugin import) -
tailwindcss()plugin call from thepluginsarray - Any "Bunny font plugin" (our restaurant site uses Google Fonts directly in Blade, so it's not needed here)
This refined setup clearly defines three Vite inputs: two distinct CSS bundles (one for your main application, one for Filament) and one JavaScript entry point.
Step 3: PostCSS for Tailwind v3 + Filament Nesting
Filament's internal CSS structure frequently uses nested rules, which requires the postcss-nesting plugin when compiling your custom theme. Update postcss.config.js accordingly to ensure these rules are processed correctly.
File: postcss.config.js
export default {
plugins: {
'tailwindcss/nesting': 'postcss-nesting',
tailwindcss: {},
autoprefixer: {},
},
};
Why nesting: Filament’s CSS internally uses nested rules. Filament’s theme documentation explicitly expects this specific PostCSS chain for proper compilation of custom themes. Without postcss-nesting, your custom Filament theme might not compile correctly, leading to broken styles.
Step 4: Restaurant Tailwind Config (Root)
Define your main application's Tailwind configuration in the project root. This includes content paths for your Blade views and JavaScript files, along with your custom brand tokens. This replaces any inefficient inline CDN configuration.
File: tailwind.config.js (located in the project root)
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./resources/views/**/*.blade.php',
'./resources/js/**/*.js',
],
theme: {
extend: {
fontFamily: {
sans: ['"Plus Jakarta Sans"', 'sans-serif'],
serif: ['"Playfair Display"', 'serif'],
},
colors: {
brand: {
orange: '#F97316',
orangeHover: '#EA580C',
dark: '#0B0F17',
darkCard: '#111827',
lightBg: '#FAFAFA',
accentGold: '#D97706',
},
},
},
},
plugins: [],
};
Classes like bg-brand-orange or font-serif will now compile at build time, and your brand tokens are centrally managed. Notice that content paths intentionally exclude Filament files; this is because admin content is scanned by the Filament-specific theme configuration, maintaining a clear separation.
Step 5: Restaurant CSS Entry (app.css)
Your main application's CSS entry point should use standard Tailwind v3 @tailwind directives. This is the file Vite will process for your main application's styles.
File: resources/css/app.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Important: This is the correct Tailwind v3 @tailwind directive syntax. It is not Tailwind v4’s @import "tailwindcss" syntax, which should be avoided when working with Filament v3.
Step 6: Filament Custom Theme CSS (theme.css)
This file is the entry point for your Filament admin panel's custom theme. It correctly imports Filament's base theme and points to its dedicated Tailwind configuration, enabling you to override or extend Filament's default styles.
File: resources/css/filament/admin/theme.css
@import '../../../../vendor/filament/filament/resources/css/theme.css';
@config 'tailwind.config.js';
The @config 'tailwind.config.js' directive is crucial; it tells this theme to use the sibling tailwind.config.js located in the same directory, ensuring Filament's styles are processed with its specific settings.
Step 7: Filament Tailwind Config (Admin Theme)
This dedicated Tailwind configuration for Filament uses Filament's official preset and includes content paths relevant only to the Filament admin panel. This ensures all Filament components are correctly styled and optimized.
File: resources/css/filament/admin/tailwind.config.js
import preset from '../../../../vendor/filament/filament/tailwind.config.preset'
export default {
presets: [preset],
content: [
'./app/Filament/**/*.php',
'./resources/views/filament/**/*.blade.php',
'./vendor/filament/**/*.blade.php',
],
}
If you add Filament plugins later, remember to append their Blade view paths to the content array here. This ensures any new classes introduced by plugins are also scanned and included in your compiled Filament theme.
Step 8: Register Theme on the Filament Panel
This is a critical step! Even with all the files in place, Filament won't use your custom theme unless it's explicitly registered in your AdminPanelProvider. This tells Filament where to find your compiled custom theme.
File: app/Providers/Filament/AdminPanelProvider.php
<?php
namespace App\Providers\Filament;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Pages;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use Filament\Widgets;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login()
->colors([
'primary' => Color::Amber,
'gray' => Color::Stone,
])
->viteTheme('resources/css/filament/admin/theme.css') // <-- THIS LINE IS CRUCIAL
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([
Pages\Dashboard::class,
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([
Widgets\AccountWidget::class,
Widgets\FilamentInfoWidget::class,
])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
]);
}
}
Without the ->viteTheme('resources/css/filament/admin/theme.css') line, Vite can successfully build the theme file, but the Filament admin panel will simply not use it, reverting to default styles.
Step 9: Blade: Remove CDN, Load Vite
Finally, update your Blade layouts to remove the old Tailwind CDN and instead load your compiled assets via Vite. This is how Laravel's asset pipeline efficiently delivers your optimized CSS and JS.
File: resources/views/layouts/app.blade.php (and welcome.blade.php)
Removed:
-
https://cdn.tailwindcss.comscript tag - Inline
tailwind.config = { ... }script tag
Added:
@vite(['resources/css/app.css', 'resources/js/app.js'])
Kept as-is (not part of this Custom Tailwind CSS fix):
- Google Fonts links (if loaded directly in Blade)
- Alpine CDN (
cdn.jsdelivr.net/.../alpinejs) -
asset('css/custom.css')/asset('js/custom.js')(if applicable for other custom assets not handled by Vite)
Step 10: Install + Production Build
With all configurations in place, it's time to install your dependencies and build your assets. This command will compile all your CSS and JS according to the Vite configuration.
npm install
npm run build
Successful build output (verified):
-
public/build/assets/app-*.css(e.g.,~36 KB) — This is your optimized CSS for the restaurant site. -
public/build/assets/theme-*.css(e.g.,~108 KB) — This is your optimized CSS for the Filament admin. -
public/build/manifest.json— This file maps both entry points, ensuring Vite correctly serves the assets with cache-busting hashes.
Streamlined Daily Workflow for Custom Tailwind CSS with Laravel and Filament
Here’s a quick reference for your daily development and deployment tasks with your new Custom Tailwind CSS with Laravel and Filament setup:
| Goal | Command |
|---|---|
| Local development (HMR) |
npm run dev or composer run dev
|
| Production / deploy | npm run build |
| Change brand colors | Edit root tailwind.config.js → npm run build
|
| Customize Filament CSS | Edit resources/css/filament/admin/theme.css → npm run build
|
| New utility classes in Blade | Keep npm run dev running, or npm run build
|
Customizing Your Tailwind Experience Further
This setup provides a solid foundation for further customization of your Custom Tailwind CSS with Laravel and Filament application.
Restaurant Brand / Theme
Modify your main application's look by editing tailwind.config.js within the theme.extend section. This is where you'd adjust fonts, colors, and other global design tokens that apply to your public-facing site.
Filament Look (Beyond Panel Colors)
While primary and gray panel colors are conveniently configured in AdminPanelProvider (e.g., Color::Amber), deeper CSS customizations for Filament can be added directly into resources/css/filament/admin/theme.css after the @import and @config lines. For example, you could target specific Filament UI elements like .fi-sidebar to apply custom styles.
New Filament Plugin Views
If you integrate new Filament plugins that introduce their own Blade views, you'll need to add the plugin’s view path to resources/css/filament/admin/tailwind.config.js within the content array. After updating, remember to npm run build to ensure these new classes are compiled into your Filament theme.
Best Practices and Anti-Patterns for Custom Tailwind CSS with Laravel and Filament
To maintain a healthy and performant Custom Tailwind CSS with Laravel and Filament project, adhere to these guidelines:
| Do | Don’t |
|---|---|
| Tailwind v3 + PostCSS for this Filament v3 app | Install @tailwindcss/vite / Tailwind v4 while on Filament v3 |
@vite for restaurant CSS |
Use cdn.tailwindcss.com
|
->viteTheme(...) for admin |
Assume public/css/filament/ is your custom theme |
| Separate app vs Filament configs | Use one shared Tailwind config for both |
| Rebuild after class/config changes | Expect CDN-style “all classes always exist” without scanning |
Final File Checklist for a Robust Setup
Here's a quick overview of the key files and their final states after implementing this playbook for your Custom Tailwind CSS with Laravel and Filament project:
-
package.json: Tailwind 3.4, no@tailwindcss/vite -
vite.config.js: 3 inputs, no Tailwind Vite plugin -
postcss.config.js:nesting+tailwind+autoprefixer -
tailwind.config.js(root): Restaurant brand + content paths -
resources/css/app.css:@tailwind base; @tailwind components; @tailwind utilities; -
resources/css/filament/admin/theme.css: Filament import +@config -
resources/css/filament/admin/tailwind.config.js: Filament preset + content -
app/Providers/Filament/AdminPanelProvider.php:->viteTheme(...)call -
resources/views/layouts/app.blade.php:@vite([...]), no Tailwind CDN -
resources/views/welcome.blade.php: Same asapp.blade.php -
public/build/manifest.json: Produced bynpm run build
Summary
Tailwind CSS v3 + PostCSS + Vite, with a separate Filament custom theme registered via viteTheme, matching Laravel 13 asset bundling and Filament v3 theme documentation.
Key Takeaways
- Always use compiled Tailwind CSS in production, never the CDN. The CDN is for development only and leads to bloated, unoptimized CSS.
- Filament v3 requires Tailwind v3; avoid mixing versions. Using Tailwind v4 plugins or syntax with Filament v3 will lead to build errors or broken styles.
- Maintain separate Tailwind configurations and entry points for your main app and Filament admin for optimal performance, clear separation of concerns, and easier maintenance.
- The
->viteTheme()method in yourAdminPanelProvideris essential for Filament to recognize and use your custom theme. Don't forget it! -
postcss-nestingis required for Filament's nested CSS rules. Ensure yourpostcss.config.jsincludes it.
GitHub Repository
Explore the full source code for the "Flavor Harbor" restaurant website, including the Filament v3 admin panel, on GitHub:
d5b94396feba3
/
fullstack-rastaurant-website-filament-laravel
A full-stack restaurant website for FLAVOR HARBOR: a public-facing dining site with menu browsing, cart checkout, and table reservations, plus a Filament admin panel for kitchen menu and CMS content.
Flavor Harbor — Restaurant Website (Laravel + Filament)
A full-stack restaurant website for FLAVOR HARBOR: a public-facing dining site with menu browsing, cart checkout, and table reservations, plus a Filament admin panel for kitchen menu and CMS content.
Stack
Layer
Technology
Backend
Laravel 13 (PHP 8.3+)
Admin
Filament 3 panel at /admin
Frontend
Blade, Alpine.js, Vite 8, Tailwind CSS 4
Database
MySQL (configurable via
.env)
Laravel
Laravel powers routing, Eloquent models, migrations, authentication for the admin panel, file storage for menu images, and the public site views. Core domain models:
- Category — menu sections (active/inactive)
- MenuItem — dishes with price, image, description, availability
- Page — CMS pages with slug, rich content, and SEO fields
- Setting — key/value site configuration (branding, hero, contact, social links)
Filament
Filament provides the Kitchen Ops admin UI (FLAVOR HARBOR | Kitchen Ops) at /admin with:
-
Kitchen Menu
- Categories (name, slug, active…
What are your experiences with Custom Tailwind CSS with Laravel and Filament? Share your tips and tricks or any challenges you faced in the comments below! If you found this guide helpful, consider following me for more in-depth technical tutorials and playbooks.













