PHPackages                             stamat/tailwind-merge-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. stamat/tailwind-merge-php

ActiveLibrary[Templating &amp; Views](/categories/templating)

stamat/tailwind-merge-php
=========================

Merge Tailwind CSS v4 class lists without style conflicts — a fast, dependency-free PHP port of tailwind-merge. Laravel-ready.

v1.0.0(1mo ago)02MITPHP ^8.2

Since Jul 6Compare

[ Source](https://github.com/stamat/tailwind-merge-php)[ Packagist](https://packagist.org/packages/stamat/tailwind-merge-php)[ RSS](/packages/stamat-tailwind-merge-php/feed)WikiDiscussions Synced 1w ago

READMEChangelog (1)Dependencies (6)Versions (2)Used By (0)

tailwind-merge-php
==================

[](#tailwind-merge-php)

[![Packagist Version](https://camo.githubusercontent.com/786fab1d0c6440b5f96a28247fb38f82caad47e37eaada00883ad6dd629e5d38/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7374616d61742f7461696c77696e642d6d657267652d706870)](https://packagist.org/packages/stamat/tailwind-merge-php)[![PHP Version](https://camo.githubusercontent.com/0fe832275237e672921caca6e381090a1e0d02f904ceedb5bdb0ed81915488ee/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f7374616d61742f7461696c77696e642d6d657267652d706870)](https://packagist.org/packages/stamat/tailwind-merge-php)[![Tailwind CSS v4](https://camo.githubusercontent.com/73120c93fe280f5191c6d850af407e4286f05bd9bc905c7ea725d38b3b4d8875/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5461696c77696e642532304353532d76342d333862646638)](https://tailwindcss.com)[![CI](https://github.com/stamat/tailwind-merge-php/actions/workflows/ci.yml/badge.svg)](https://github.com/stamat/tailwind-merge-php/actions/workflows/ci.yml)[![License](https://camo.githubusercontent.com/28cfa58ca890ecb2f99894ae75c7df55bc5b51afc98c0652a8fd2616d5334244/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7374616d61742f7461696c77696e642d6d657267652d7068703f636c656172)](LICENSE)

Merge [Tailwind CSS](https://tailwindcss.com) class lists without style conflicts — a fast, independent PHP port of [dcastil/tailwind-merge](https://github.com/dcastil/tailwind-merge), ported directly from the JS source and not a fork of any existing PHP package.

> If you are building a complex UI — you should componentize. And if you are using Tailwind and componentizing — you need Tailwind Merge.

Laravel glue ships in the box, so it's **ready for Laravel** too. The name `tailwind-merge-php` is deliberate: this is a framework-agnostic PHP package first, and a Laravel plugin second.

- **Tailwind CSS v4 only.** (to v4.3) No legacy v3 config (no custom separators, TW3 theme keys, etc.). The v3-style leading `!` important modifier is still parsed, matching upstream.
- **Fast.** Class-map trie built once per instance, native string functions on the hot path, in-process LRU result cache. Roughly 50× faster than [`gehrisandro/tailwind-merge-php`](https://github.com/gehrisandro/tailwind-merge-php) on the same real-world class lists even with the cache disabled, far more with a warm cache (note: that port targets Tailwind v3, this one v4). The benchmark itself was deliberately removed from the repo — it isn't part of the shipped package or the test contract.
- **Defensive.** `strict_types`, final classes, PHPStan level max, invalid config keys and non-string class list entries rejected with `InvalidArgumentException`.
- **Test-based.** The behavior contract is the upstream JS test suite: 371 tests / ~1,900 assertions, including 323 merge cases extracted from and verified against the real `tailwind-merge` npm package v3.6.0 (which targets Tailwind CSS v4).

Usage
-----

[](#usage)

```
use Stamat\TailwindMerge\TailwindMerge;

// Shared instance, default Tailwind v4 config
TailwindMerge::instance()->merge('px-2 py-1 bg-red-500 hover:bg-red-700', 'p-3 bg-[#B91C1C]');
// => 'hover:bg-red-700 p-3 bg-[#B91C1C]'

// Strings and nested arrays; null/false entries are skipped
TailwindMerge::instance()->merge('block', ['px-2', $isLarge ? 'text-lg' : null]);
```

### Custom config

[](#custom-config)

```
// Extend / override the default config (same semantics as extendTailwindMerge in JS)
$tw = TailwindMerge::create([
    'cacheSize' => 1000,
    'prefix' => 'tw',                       // for Tailwind's `tw:` prefix option
    'extend' => [
        'theme' => ['spacing' => ['my-space']],
        'classGroups' => ['shadow' => ['shadow-100', 'shadow-200']],
    ],
    'override' => [ /* replaces instead of appends */ ],
]);

// Or a fully custom config (see DefaultConfig::config() for the shape)
$tw = new TailwindMerge($fullConfig);
```

Laravel
-------

[](#laravel)

Framework glue ships in the box and is auto-discovered — no manual provider registration. Install `illuminate/support` (already present in any Laravel app).

```
{{-- Blade directive --}}
…
```

```
use Stamat\TailwindMerge\Laravel\Facades\TwMerge;
use Stamat\TailwindMerge\TailwindMerge;

TwMerge::merge('px-2 p-4', 'bg-red');   // facade
app(TailwindMerge::class)->merge(...);   // or resolve from the container
```

The container binds a single shared, configured instance. In a Laravel app, always resolve through the facade or container (as above) — **not**`TailwindMerge::instance()`. That static helper returns a separate instance built from the *default* config, ignoring anything you set in `config/tailwind-merge.php` (e.g. `prefix`, `cacheSize`), so the two can merge differently.

Publish the config to tune `cacheSize`, `prefix`, `extend` and `override`:

```
php artisan vendor:publish --tag=tailwind-merge
```

Development
-----------

[](#development)

```
composer install
composer test          # pest
composer test:types    # phpstan (level max)
composer lint          # pint
```

`src/DefaultConfig.php` is generated from the upstream JS package — do not edit by hand. The upstream version is pinned in `bin/package-lock.json` and stamped into the generated file header; bump `bin/package.json` to move it. CI regenerates and fails on drift.

```
cd bin && npm install && node generate-default-config.mjs
```

`tests/Fixtures/merge-cases.json` holds merge cases extracted from the upstream test suites and verified against the real JS implementation.

Disclaimer
----------

[](#disclaimer)

**This repo was built with AI** — specifically Claude **Fable 5** — as a model test, with explicit instructions to follow best practices such as strict test-driven and defensive coding.

I know there are several PHP ports of [tailwind-merge](https://packagist.org/search/?query=tailwind-merge), but after reviewing them, I've seen the areas they could be improved upon, so I decided to synthesize my own. I do need it for a project or two.

The main improvement is that the tests come straight from upstream and are treated as law: the behavior contract is the JS `tailwind-merge` test suite itself, not a hand-picked subset. If upstream and this port disagree, upstream is right and the port is the bug.

With 🫀 @stamat

License
-------

[](#license)

MIT, @stamat. Ported from [tailwind-merge](https://github.com/dcastil/tailwind-merge) (MIT, © Dany Castillo).

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance90

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

48d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b48daf14c3d85ea837b3c55d813c6078b9e958fd6ad63953149d56e3a8e45763?d=identicon)[stamat](/maintainers/stamat)

---

Tags

phplaravelcssbladetailwindmergeclassestailwindcsstailwind-mergetailwind-css-v4

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/stamat-tailwind-merge-php/health.svg)

```
[![Health](https://phpackages.com/badges/stamat-tailwind-merge-php/health.svg)](https://phpackages.com/packages/stamat-tailwind-merge-php)
```

###  Alternatives

[gehrisandro/tailwind-merge-laravel

TailwindMerge for Laravel merges multiple Tailwind CSS classes by automatically resolving conflicts between them

342974.3k36](/packages/gehrisandro-tailwind-merge-laravel)[yieldstudio/tailwind-merge-php

Merge Tailwind CSS classes without style conflicts

4975.8k1](/packages/yieldstudio-tailwind-merge-php)[electrik/slate

Slate - a Laravel Blade UI Kit is a set of anonymous blade components built using TailwindCSS v4 with built-in dark mode support for your next Laravel project

102.5k1](/packages/electrik-slate)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
