Components, @props and Vite Integration
Laravel Blade brings its own composition system for server rendered views through components, attribute bags and @props, and Tailwind CSS adds a consistent utility system for styling on top. Setting up Tailwind CSS cleanly in Laravel Blade projects gives you reusable components, a lean Vite build pipeline and a CSS bundle that only contains the classes actually in use.
Table of Contents
- 1. Why Tailwind CSS and Blade components fit together
- 2. Vite setup: integrating Tailwind CSS into Laravel
- 3. Building Blade components with Tailwind classes
- 4. @props and typed variants for components
- 5. Attribute bags: merging classes from outside
- 6. Anonymous components and slots for layout blocks
- 7. Content configuration: scanning Blade files correctly
- 8. Class components versus anonymous components in practice
- 9. Tailwind CSS in Laravel Blade compared to other approaches
- 10. Summary
- 11. FAQ
1. Why Tailwind CSS and Blade components fit together
Laravel Blade is a server side templating engine that compiles views into plain PHP and brings its own syntax for conditionals, loops and components. Tailwind CSS with Laravel Blade works so well together because Blade components deliver exactly the encapsulation unit that utility classes need to avoid being scattered across an entire application. Instead of defining a button class in a global stylesheet, you encapsulate the full utility chain once in an x-button component and call it consistently everywhere in the project.
The second reason Tailwind CSS in Laravel projects is so common comes down to the tight integration with Vite as the build tool. Since version 9, Laravel ships an official Vite integration that supports hot module replacement for Blade views and wires the Tailwind compiler directly into the dev server. Changes to utility classes in a Blade file show up in the browser without a manual reload, which shortens the feedback loop while building components and makes the combination of PHP backend and Tailwind frontend noticeably more productive.
2. Vite setup: integrating Tailwind CSS into Laravel
Integrating Tailwind CSS into Laravel runs through the official Vite plugin @tailwindcss/vite, registered in vite.config.js next to the Laravel plugin. This configuration replaces the previously common PostCSS pipeline with a separate tailwind.config.js and postcss.config.js, because Tailwind CSS 4 handles the entire build process through a single Vite plugin. For a fresh Laravel project a single CSS import in the main stylesheet file, pulled in through the Blade directive @vite in the layout, is enough.
A common mistake when setting up Tailwind CSS with Laravel is that developers still expect a content configuration like in Tailwind CSS 3. Tailwind CSS 4 scans project files automatically through heuristics tuned to common directory structures, and in a Laravel project this usually works without any extra configuration. Only with unusual directory structures, for example domain driven design with Blade files outside resources/views, does the scan path need to be extended explicitly via @source.
# Install Tailwind CSS 4 and the Vite plugin in a Laravel project
npm install tailwindcss @tailwindcss/vite
# vite.config.js
// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
tailwindcss(),
],
});
/* resources/css/app.css — imported once via @vite in the base layout */
@import "tailwindcss";
@theme {
--color-brand-500: oklch(0.60 0.20 255);
--font-sans: "Inter", system-ui, sans-serif;
}
3. Building Blade components with Tailwind classes
A Blade component consists of a PHP class under app/View/Components and a matching Blade file under resources/views/components. For Tailwind CSS in Blade components the class is usually minimal, defining constructor parameters and returning the associated view, while the actual structure and utility classes live entirely in the Blade file. This separation lets you bundle design decisions in a single place instead of repeating utility chains in every call site.
The practical benefit of Tailwind CSS with Blade components shows most clearly with frequently recurring UI elements like buttons, cards or badges. Instead of copying the same long utility chain for a button across twenty views, you encapsulate it once in <x-button>. If the design changes later, one adjustment in the component file is enough instead of manually walking through twenty views. This pattern reduces interface inconsistencies significantly and makes refactors low risk.
4. @props and typed variants for components
The Blade directive @props defines which attributes a component accepts as its own typed properties instead of passing them through unfiltered as HTML attributes. For Tailwind CSS in Laravel Blade components this pattern is central because it lets you define a variant property that internally maps to a fixed set of complete utility classes. That keeps the Tailwind scanner able to statically detect all class names, since they sit as full strings in a PHP match expression rather than as fragments assembled at runtime.
A second benefit of @props in Tailwind CSS with Blade is the ability to define default values. A button component might declare @props(['variant' => 'primary', 'size' => 'md']), so every call without explicit arguments automatically gets the primary, medium sized variant. That cuts boilerplate in the calling views significantly and makes the component API self documenting, because the available variants are readable directly from the @props line.
{{-- resources/views/components/button.blade.php --}}
@props([
'variant' => 'primary',
'size' => 'md',
])
@php
// Full, static class strings — never built at runtime
$variants = [
'primary' => 'bg-sky-600 text-white hover:bg-sky-700',
'secondary' => 'bg-slate-100 text-slate-800 hover:bg-slate-200',
'danger' => 'bg-red-600 text-white hover:bg-red-700',
];
$sizes = [
'sm' => 'px-3 py-1.5 text-sm',
'md' => 'px-4 py-2 text-base',
'lg' => 'px-6 py-3 text-lg',
];
@endphp
<button {{ $attributes->merge(['class' => "rounded-lg font-semibold transition-colors {$variants[$variant]} {$sizes[$size]}"]) }}>
{{ $slot }}
</button>
5. Attribute bags: merging classes from outside
The attribute bag $attributes collects every HTML attribute passed when a component is invoked but not explicitly declared through @props. For Tailwind CSS in Blade the method $attributes->merge(['class' => '...']) is the central tool for combining a component's default classes with extra classes a calling view supplies. Important detail: merge combines classes additively, it does not overwrite them, so base component classes should be chosen so that extra classes complement them sensibly instead of colliding with them.
A proven pattern with Tailwind CSS and attribute bags is to leave layout related classes like margin or grid placement to the caller via $attributes, while the component itself only defines its intrinsic styles like color, padding and radius. That keeps the component reusable across different layout contexts without every usage site rewriting the entire utility chain. For conditional classes that depend on a boolean property, $attributes->class(['ring-2 ring-red-500' => $hasError]) is a good fit, adding or omitting classes based on the condition.
{{-- Caller adds layout classes, component keeps intrinsic styles --}}
<x-button variant="primary" class="w-full mt-4">
Save changes
</x-button>
{{-- Conditional classes based on a boolean prop --}}
@props(['hasError' => false])
<input {{ $attributes->class([
'w-full rounded-lg border px-3 py-2',
'border-red-500 ring-2 ring-red-200' => $hasError,
'border-slate-300' => ! $hasError,
]) }} />
6. Anonymous components and slots for layout blocks
Alongside class components with their own PHP file, Blade also supports anonymous components consisting only of a Blade file without an associated PHP class. For Tailwind CSS in Laravel, anonymous components are especially well suited to pure layout blocks like cards, containers or grid wrappers that need no complex logic, only a fixed utility structure with slots for variable content. Named slots via <x-slot:header> let you give a card component a separately styled header and body area without redefining the structure on every use.
For deeply nested anonymous components, for example a card inside a grid inside a page layout, it helps to give each layer its own clearly bounded responsibility. The outermost layer handles page margins and maximum width, the middle layer handles grid columns and gaps, the innermost layer handles the visual appearance of the card itself. This layering prevents Tailwind CSS with Blade components from collapsing into a single unwieldy utility chain per element, keeping it cleanly distributed across several focused components instead.
7. Content configuration: scanning Blade files correctly
Tailwind CSS 4 detects Blade files by default through automatic content detection, which includes directories like resources/views without extra configuration. In Tailwind CSS Laravel Blade projects with an unusual structure, for example modular packages under packages/*/resources/views, the scan scope must be extended explicitly via the @source directive in the main stylesheet, otherwise classes from these directories are missing from the final bundle. A common pitfall is that generated Blade cache files under storage/framework/views could also get scanned, letting stale class names leak into the bundle if the cache isn't cleared regularly.
For third party packages that ship their own Blade components with Tailwind classes, for example admin panel libraries, their vendor directory must also be included in content detection, otherwise classes from the package get stripped during the build and the interface visually breaks. Tailwind CSS with Laravel projects combining several packages should therefore regularly check whether new vendor paths need adding to the @source list after a Composer update.
/* resources/css/app.css — extend content detection for non-standard paths */
@import "tailwindcss";
@source "../../packages/**/resources/views/**/*.blade.php";
@source "../../vendor/some/admin-package/resources/views/**/*.blade.php";
8. Class components versus anonymous components in practice
The choice between class components with PHP logic and purely anonymous components is not a matter of style in Tailwind CSS Blade projects, it depends on the complexity of the variant logic. As soon as a component combines more than two or three conditional utility chains, for example variant, size and state at once, a PHP class with clearly named methods becomes more readable than nested Blade directives in the template file. For simple layout blocks without variant logic, an anonymous component is entirely sufficient and saves an extra PHP file per component.
A pattern that has proven itself in larger Tailwind CSS Laravel projects is a small number of class components for complex, frequently reused elements like buttons, form fields and badges, complemented by many anonymous components for page specific layout blocks. This split keeps the number of PHP classes manageable while the utility structure stays consistent across the whole project, because the most important recurring elements are centrally defined in class components.
9. Tailwind CSS in Laravel Blade compared to other approaches
Compared to other templating approaches for PHP applications, the combination of Tailwind CSS and Laravel Blade strikes its own balance between server side simplicity and modern utility styling practice. The following table contrasts the key differences with classic PHP templates without a component system as well as with client side frameworks.
| Aspect | Classic PHP template | Tailwind CSS with Blade | Advantage |
|---|---|---|---|
| Reuse | Copy paste of markup blocks | Blade components with @props | One place to change instead of many |
| Adjusting classes from outside | Manual string concatenation | $attributes->merge() / class() | Safe, additive class logic |
| Build pipeline | Separate Gulp or Mix setup | Native Vite plugin | Fewer configuration layers |
| Client side interactivity | Full SPA framework needed | Alpine.js for isolated islands | No extra build for a JS framework |
| CSS bundle size | One global, often bloated stylesheet | Only classes actually used | Smaller payload per page |
For teams coming from plain PHP with manually maintained stylesheets, the key mindset shift with Tailwind CSS in Laravel Blade is that styling decisions get made in components instead of global CSS files. That changes not just the file structure but the review process too, because design changes are now visible locally in a component instead of disappearing into distant stylesheet rules.
Mironsoft
Laravel, Tailwind CSS and maintainable Blade component libraries
Building a Laravel project with Tailwind CSS and Blade components?
We build Laravel frontends with Tailwind CSS, cleanly structured Blade components and a lean Vite pipeline, from the first component library to a full admin interface.
Component library
Reusable Blade components with @props and attribute bags
Vite setup
Configuring Tailwind CSS 4 and the Laravel Vite plugin cleanly
Migration
Moving existing Laravel views to component based styling
10. Summary
Tailwind CSS with Laravel Blade complements itself so well because both systems rely on composition: Blade components encapsulate markup structure, Tailwind utility classes encapsulate visual decisions, and @props together with attribute bags connects both into a clean, typed component API. The Vite integration ensures the build process needs no separate PostCSS configuration and changes show up in the browser instantly.
Anyone using Tailwind CSS in Laravel in production should watch for complete, static class names in @props variants, consistently let layout classes be supplied from outside through attribute bags, and explicitly extend content detection for unusual directory structures or vendor packages. That keeps the combination of Laravel Blade and Tailwind CSS consistent and easy to maintain across many components and views.
Tailwind CSS with Laravel Blade — The Essentials at a Glance
Setup
Register @tailwindcss/vite next to the Laravel Vite plugin, a single CSS import via @vite in the layout is enough.
Components and @props
Define variants as complete, static class strings in @props, never assemble them at runtime.
Attribute bags
$attributes->merge() and $attributes->class() for additive, conditional class logic from outside.
Content scan
Explicitly include unusual directories and vendor packages in detection via @source.