laravel-frontend-presets/tailwindcss
A Laravel frontend preset that swaps the default Bootstrap scaffolding for Tailwind CSS, publishing configured auth views, layouts, and frontend assets so new applications start with a clean utility-first design out of the box.
Know the shelf life first. This preset is designed for Laravel 5.5–7.x, because it plugs into the php artisan preset command that Laravel removed in 8.0. Release 4.3.1 relaxed the laravel/ui constraint to include Laravel 8.x, but that only makes the package installable on 8.x — it does not restore the preset command. The last substantive release was Sept 2020. On Laravel 8+ use laravel/breeze instead:
composer require laravel/breeze --dev && php artisan breeze:install
On a legacy 7.x app, this is the whole install:
composer require laravel-frontend-presets/tailwindcss --dev
php artisan preset tailwindcss # assets only
# or
php artisan preset tailwindcss-auth # assets + auth views/routes scaffolding
npm install && npm run dev
Where to look first, in this order:
| File | Why |
|---|---|
webpack.mix.js |
The preset rewrites it — that's where Tailwind's PostCSS step gets wired in |
tailwind.config.js |
Generated at project root; your day-to-day home |
resources/sass/app.scss (or resources/css/app.css) |
Tailwind entrypoint with the @tailwind directives |
resources/views/layouts/app.blade.php |
The shell every scaffolded page extends |
resources/views/auth/* |
Only if you used tailwindcss-auth |
package.json |
Bootstrap/jQuery removed, Tailwind added — diff this before trusting it |
First use case: run tailwindcss-auth, confirm Auth::routes() is in routes/web.php, hit /register, and you have working Tailwind-styled login/register/password-reset views in about two minutes. From there, delete the demo markup in layouts/app.blade.php and build your real layout on top of it.
Once scaffolded, the package has done its job — the assets are copied into your app, not referenced. You can composer remove laravel-frontend-presets/tailwindcss --dev and nothing breaks.
Treat it as a stub generator, not a dependency. Everything the package does is a one-shot copy of stub files. That means the real workflow is: run preset → commit the generated files → customize freely → never run it again (re-running overwrites your edits).
Pick the preset granularity deliberately.
php artisan preset tailwindcss — layout + compiled CSS pipeline only.php artisan preset tailwindcss-auth — adds resources/views/auth/*, layouts/app.blade.php, home.blade.php, and the Auth::routes() wiring.php artisan preset none — Laravel's built-in escape hatch; strips the frontend scaffolding without uninstalling anything. Useful when you want a clean slate but the preset's view files are still hanging around.Extend the config, don't fork the markup. The durable customization surface is tailwind.config.js:
module.exports = {
theme: {
extend: {
colors: { brand: { DEFAULT: '#4f46e5', dark: '#3730a3' } },
fontFamily: { sans: ['Inter', 'system-ui', 'sans-serif'] },
},
},
variants: { extend: { opacity: ['disabled'], cursor: ['disabled'] } },
plugins: [require('@tailwindcss/forms')],
};
Then use bg-brand / text-brand-dark across every Blade view. This survives view rewrites; hardcoded hex in Blade does not.
Collapse repetition into Blade components, not @apply soup. The preset gives you flat markup, so immediately extract:
{{-- resources/views/components/button.blade.php --}}
<button {{ $attributes->merge(['class' => 'px-4 py-2 rounded bg-brand text-white hover:bg-brand-dark']) }}>
{{ $slot }}
</button>
Reserve @apply for things Blade components can't express (e.g. third-party widget overrides) and keep those in a small resources/sass/_components.scss imported after the @tailwind directives. Mixing heavy @apply with a purge config is a common source of "why did my CSS disappear in production".
Production purge is mandatory, and it's the one thing that bites everyone. The generated config targets Tailwind 1.x and uses the purge key, not content:
purge: {
enabled: Mix.inProduction(),
content: [
'./resources/**/*.blade.php',
'./resources/**/*.js',
'./resources/**/*.vue',
],
},
Add globs for any directory you generate views into (package views, Livewire, notification templates). A missing glob silently drops classes in npm run prod while npm run dev looks perfect.
Mix wiring. The preset typically ends up as:
mix.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css', [
require('tailwindcss'),
require('autoprefixer'),
]);
If you're on Laravel Mix 6+, replace the array with mix.tailwindcss(). If your app is an Inertia/Vue SPA, the same pipeline works — just make sure resources/js/**/*.vue is in the purge globs and that component styles use <style lang="postcss"> (an @apply in a plain <style> block won't be processed by Tailwind).
Auth view customization loop. Edit resources/views/auth/login.blade.php etc. directly — there's no vendor:publish to fight. If you need the mail-side templates to match, php artisan vendor:publish --tag=laravel-notifications and restyle those with the same classes. After any view edit: php artisan view:clear.
The preset command simply doesn't exist on Laravel 8+. You'll get Command "preset" is not defined. There is no backport, and 4.3.1 deliberately did not add one — it only widened the laravel/ui constraint so Composer resolves on 8.x. The practical consequence is worse than before: composer require now succeeds on a Laravel 8 project, and the failure is deferred to the artisan call, after laravel/ui has already been pulled into your dependency tree. If you see laravel/ui appear in composer.lock unexpectedly, this package put it there. Don't mistake the release note for restored 8.x support — it's a dependency-constraint bump, not a feature.
If you're on 8.x and already have laravel/ui from this package, you can salvage the situation without uninstalling: run php artisan ui:auth (first-party, Bootstrap or Vue flavoured markup) and then hand-port the preset's Tailwind classes from its stubs in vendor/laravel-frontend-presets/tailwindcss/src/stubs/. That is genuinely the fastest path if you can't upgrade to Breeze, but it is a manual port, not a preset invocation.
It overwrites files without asking. webpack.mix.js, package.json, resources/js/bootstrap.js, and the Sass entrypoint are all replaced or rewritten. Always: clean git status → run preset → read the diff. If you had custom Mix configuration (browserSync, versioning, custom entrypoints), re-apply it by hand afterwards.
Leftover Bootstrap references are common. The preset removes Bootstrap from package.json but resources/js/bootstrap.js sometimes still contains window.$/Popper wiring, and resources/sass/_variables.scss may linger. Grep for bootstrap, jquery, and popper after installing, or you'll get confusing npm run dev errors about missing modules.
Never build class names dynamically. 'bg-' . $status or text-{{ $color }}-500 will be purged in production. Use full literals, or a lookup map, or (Tailwind 2+) a safelist. Tailwind 1.x has no safelist — you must use a map object with complete class strings.
You'll want to upgrade Tailwind, and that's a rewrite of the config. The generated tailwind.config.js is 1.x-era. Moving to 2/3/4 means: purge → content, variants semantics changed, @tailwind directives still fine but the entrypoint location may need to move from resources/sass to resources/css (Dart Sass + Tailwind 3 is fine, but node-sass will choke on newer Node). Plan a half-day for the migration and rebuild the config from the official upgrade guide rather than patching.
Debugging a missing class in prod: don't guess — run the Tailwind CLI directly against your entrypoint and inspect the output:
npx tailwindcss -i resources/css/app.css -o /tmp/out.css --minify
If the class isn't in /tmp/out.css, it's a purge/content glob problem, not a Mix problem. Nine times out of ten it's a missing glob or a concatenated class name.
Mix/Node version skew. The stubs assume Laravel Mix 4/5 and an older Node. On Node 18+ with Mix 6/webpack 5, expect to rewrite webpack.mix.js and pin postcss/autoprefixer explicitly. Don't chase the preset's original dependency versions — modernize the toolchain and keep only the file layout.
No uninstall path. composer remove leaves every generated file behind. If you need to bail, git checkout . is your revert — another argument for committing before you run it. On 8.x, remember to also composer remove laravel/ui if this package dragged it in and you aren't using it.
Extension point: copy it, don't depend on it. If your team has a house style (your layout, your components, your Tailwind config), fork the package's src/stubs into an internal acme/frontend-preset and register your own preset name. The package is ~a few hundred lines of stub-copying; vendoring it gives you a repeatable php artisan preset acme for new Laravel 7 projects without depending on a barely-maintained repo.
Realistic verdict: 4.3.1 is a one-line constraint bump, not a revival. For a greenfield 2026 project, this package is a reference implementation — read its stubs for how to structure a Tailwind layout and auth views, then use Breeze. For maintaining an existing Laravel 7 app, it's a legitimate 10-minute scaffolding win, provided you commit first and budget an hour to clean up Bootstrap remnants and set up purge correctly. On an 8.x app, treat the new constraint as a trap rather than an invitation.
How can I help you explore Laravel packages today?