gehrisandro/tailwind-merge-php
Merge Tailwind CSS class strings in PHP with automatic conflict resolution (last class wins), ported from tailwind-merge. Supports Tailwind v3.0–v3.4, configurable and cacheable. Requires PHP 8.1+.
Installation:
composer require gehrisandro/tailwind-merge-php
Ensure your project uses PHP 8.1+ and Tailwind CSS v3.0–v3.4.
First Use Case: Resolve a simple class conflict in a Blade template or Livewire component:
use TailwindMerge\TailwindMerge;
$mergedClasses = TailwindMerge::instance()->merge('text-red-500', 'text-blue-500');
// Output: 'text-blue-500'
Use the result directly in Blade:
<div class="{{ $mergedClasses }}">Merged Text</div>
Where to Look First:
TailwindMerge::instance(): Default singleton for quick merges.TailwindMerge::factory(): For custom configurations or caching.Dynamically merge classes based on runtime logic (e.g., user roles, themes):
@php
$baseClasses = 'bg-gray-100 hover:bg-gray-200';
$userClasses = 'dark:bg-blue-800';
$merged = TailwindMerge::instance()->merge($baseClasses, $userClasses);
@endphp
<div class="{{ $merged }}">Dynamic Background</div>
Resolve conflicts in real-time updates without client-side flicker:
// Livewire Component
public function updateClasses()
{
$this->mergedClasses = TailwindMerge::instance()->merge(
'p-4 border border-gray-300',
'!p-6 border-red-500'
);
// Output: '!p-6 border-red-500'
}
<div class="{{ $mergedClasses }}">Livewire Merged</div>
Standardize class merging across shared components:
// Button Component
public function render()
{
$classes = TailwindMerge::instance()->merge(
'px-4 py-2 rounded',
$this->variant === 'primary' ? 'bg-blue-600 text-white' : 'bg-gray-200'
);
return view('components.button', ['classes' => $classes]);
}
Merge classes in server-rendered responses (e.g., emails, PDFs):
$emailClasses = TailwindMerge::instance()->merge(
'text-lg font-sans',
$user->prefersDarkMode ? 'dark:text-gray-300' : 'text-gray-800'
);
$mailer->send([...], view('emails.welcome', ['classes' => $emailClasses]));
Resolve tenant/user-specific styles:
$tenantClasses = TailwindMerge::instance()->merge(
'bg-white text-black',
Tenant::find($id)->themeClasses
);
Enable PSR-16 caching (e.g., Laravel’s cache) for high-frequency merges:
use Illuminate\Cache\CacheManager;
$cache = app(CacheManager::class)->store('file');
$cachedMerge = TailwindMerge::factory()->withCache($cache)->make();
Clear cache when updating Tailwind config:
php artisan cache:clear
Extend default class groups for custom utilities:
$customMerge = TailwindMerge::factory()->withConfiguration([
'classGroups' => [
'font-size' => [
['text' => ['custom-large']]
],
],
])->make();
Reference: Original Config Docs.
Handle dynamic classes like CSS variables:
$merged = TailwindMerge::instance()->merge(
'bg-[var(--primary)]',
'bg-blue-500'
);
// Output: 'bg-blue-500' (if no var(--primary) is defined)
Merge hover/focus states reliably:
$merged = TailwindMerge::instance()->merge(
'hover:bg-gray-200',
'focus:bg-blue-100'
);
// Output: 'hover:bg-gray-200 focus:bg-blue-100'
Preserve non-Tailwind classes while merging:
$merged = TailwindMerge::instance()->merge(
'custom-class block',
'inline'
);
// Output: 'custom-class inline'
Configuration Mismatch:
classGroups in the configuration:
'classGroups' => [
'colors' => [
['bg' => ['custom-blue']],
],
],
TailwindMerge::instance()->getConfiguration() to verify loaded rules.Arbitrary Value Conflicts:
z-[999], [&>*]:underline) may not merge as expected.// tailwind.config.js
module.exports = {
corePlugins: {
preflight: false,
},
important: true,
variants: {
extend: {},
},
plugins: [],
};
Caching Stale Data:
$cacheKey = 'tailwind_merge_' . md5(file_get_contents(config_path('tailwind.php')));
$cache->remember($cacheKey, now()->addHours(1), fn() => $mergedClasses);
PHP 8.1+ Requirement:
php.ini or use a Docker/PHP-FPM image with PHP 8.1+.Dark Mode Quirks:
dark:...) may not merge predictably if the base class is missing.$merged = TailwindMerge::instance()->merge(
'text-black dark:text-white',
'dark:text-gray-700'
);
// Output: 'text-black dark:text-gray-700'
Important Modifier (!):
! modifier may not behave as expected in all contexts.! classes last in the merge order:
$merged = TailwindMerge::instance()->merge(
'font-medium',
'!font-bold'
);
// Output: '!font-bold'
Inspect Merged Classes:
Use TailwindMerge::instance()->debugMerge() to see intermediate steps:
$debug = TailwindMerge::instance()->debugMerge('p-4 px-6');
// Output: ['p-4', 'px-6'] → ['px-6']
Validate Tailwind Config:
Ensure your tailwind.config.js matches the package’s assumptions:
red-500, not primary-500).bg-, text-, not custom-bg-).Test Edge Cases:
lg:p-4 md:p-6 → lg:p-6 md:p-6.hover:text-red-500 focus:text-blue-500 → merged as-is.z-10 z-[999] → z-[999].Fallback for Unsupported Cases: If merging fails, fall back to the last class:
$merged = TailwindMerge::instance()->merge(...);
$fallback = end(explode(' ',
How can I help you explore Laravel packages today?