gehrisandro/tailwind-merge-laravel
Merge Tailwind CSS classes in Laravel and automatically resolve conflicts (later classes win). Ideal for Blade components and directives. PHP/Laravel port of tailwind-merge. Supports Tailwind v3.0–v3.3 (Laravel 10+).
Installation:
composer require gehrisandro/tailwind-merge-laravel
No additional steps required for basic usage.
First Use Case:
In a Blade component, replace static class attributes with twMerge:
<!-- components/Button.blade.php -->
<button {{ $attributes->twMerge('px-4 py-2 bg-blue-500 text-white rounded') }}>
{{ $slot }}
</button>
Usage in a view:
<x-button class="bg-green-500 hover:bg-green-600">Click</x-button>
Output:
<button class="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600">Click</button>
TailwindMerge::merge('class1 class2')@twMerge('class1 class2')twMerge('class1 class2')$attributes->twMerge('base-classes')<!-- components/Card.blade.php -->
<div {{ $attributes->twMerge('p-6 rounded-lg shadow-md') }}>
{{ $slot }}
</div>
Usage:
<x-card class="bg-blue-100 border border-blue-200">
Content with merged classes
</x-card>
<!-- components/Alert.blade.php -->
<div {{ $attributes->withoutTwMergeClasses()->twMerge('p-4 rounded') }}>
<div {{ $attributes->twMergeFor('icon', 'h-5 w-5') }}></div>
<div>{{ $slot }}</div>
</div>
Usage:
<x-alert class="bg-yellow-100" class:icon="text-yellow-500">
Warning!
</x-alert>
<!-- components/Toggle.blade.php -->
<button
{{ $attributes->twMerge(
'px-4 py-2 rounded',
$active ? 'bg-green-500 text-white' : 'bg-gray-200 text-gray-800'
) }}
>
{{ $slot }}
</button>
<!-- components/ThemeButton.blade.php -->
<button
{{ $attributes->twMerge(
'px-4 py-2 rounded',
config('theme.colors.primary') . '-500 text-white'
) }}
>
{{ $slot }}
</button>
<div class="{{ twMerge('bg-white p-4', $legacyClasses) }}">
<!-- Content -->
</div>
// In a Form Request or Controller
$mergedClasses = twMerge('border border-gray-300', $userInput['classes'] ?? '');
return view('form', ['classes' => $mergedClasses]);
// config/tailwind-merge.php
return [
'classGroups' => [
'custom-colors' => [
['bg' => ['brand-primary', 'brand-secondary']],
['text' => ['brand-primary', 'brand-secondary']],
],
],
];
Usage:
<x-button class="bg-brand-primary">Brand Button</x-button>
Non-Tailwind Classes:
custom-class) are preserved but may cause unexpected behavior if they conflict with Tailwind’s internal parsing.classGroups or prefix them (e.g., tw-custom-class).Arbitrary Values:
z-[999] or w-[calc(100%-2rem)] are resolved last but may conflict with non-arbitrary classes.Blade Directive Caching:
@twMerge directives are not cached by default. For performance-critical views, use the facade or helper instead:
{{ TailwindMerge::merge('classes') }}
Component Attributes Limitation:
$attributes->merge() is unsupported. Always use twMerge for class merging in components.Dark Mode Conflicts:
dark:bg-gray-800) are resolved independently. Override them explicitly:
<div class="{{ twMerge('bg-white dark:bg-gray-800', '!bg-red-500') }}">
<!-- Forces bg-red-500 even in dark mode -->
</div>
Inspect Merged Output:
Use dd(twMerge('classes')) to debug the merged result before rendering.
Validate Tailwind Config:
If merging fails, ensure your tailwind.config.js aligns with the package’s assumptions (e.g., no conflicting color names like bg-primary when primary is a font-weight class).
Check for Typos: The package is strict about Tailwind class syntax. Validate classes using the Tailwind CSS Playground.
Custom Class Groups:
For unsupported classes, extend classGroups in config/tailwind-merge.php:
'classGroups' => [
'my-custom' => [
['my-prefix' => ['class1', 'class2']],
],
],
Cache Facade Results: Cache merged classes in a service or view composer for reusable components:
// app/View/Composers/TailwindMerger.php
public function compose($view)
{
$view->with('mergedClasses', Cache::remember('merged.classes', now()->addHours(1), function () {
return twMerge('base-classes', $dynamicClasses);
}));
}
Avoid Redundant Merging: Merge classes once in the component and pass the result to child components:
<!-- Parent Component -->
<x-child classes="{{ twMerge('base-classes', $parentClasses) }}" />
Custom Merge Logic:
Override the merge behavior by extending the underlying TailwindMerge class:
use TailwindMerge\Laravel\Facades\TailwindMerge;
class CustomTailwindMerge extends TailwindMerge
{
protected function resolveConflicts(array $classes): array
{
// Custom logic here
return parent::resolveConflicts($classes);
}
}
Bind it in a service provider:
$this->app->bind('tailwind-merge', function () {
return new CustomTailwindMerge();
});
Blade Directive Aliases:
Register additional directives in AppServiceProvider:
Blade::directive('merge', function ($expression) {
return "<?php echo twMerge({$expression}); ?>";
});
Usage:
@merge('classes')
Integration with Livewire/Alpine:
Use twMerge in Alpine.js or Livewire components to dynamically update classes:
<div x-data="{ active: false }" x-bind:class="twMerge('p-4', active ? 'bg-green-500' : 'bg-gray-200')">
Toggle me
</div>
How can I help you explore Laravel packages today?