UseClassy transforms Tailwind variant attributes (class:hover="...") into standard Tailwind classes (hover:...). This allows for cleaner component markup by separating base classes from stateful or responsive variants.
- Transforms attributes like
class:hover="text-blue-500"to standardclass="hover:text-blue-500". - Supports chaining modifiers like
class:dark:hover="text-blue-500". - Works seamlessly with React (
className), Vue/HTML (class), and Svelte (class). - Integrates with Vite's build process and dev server. No runtime overhead.
- Smart Caching: Avoids reprocessing unchanged files during development.
- Runs before Tailwind JIT compiler with HMR and TailwindMerge support.
# npm
npm install vite-plugin-useclassy --save-dev
# yarn
yarn add vite-plugin-useclassy -D
# pnpm
pnpm add vite-plugin-useclassy -DWhen using the React helpers (vite-plugin-useclassy/react), install React 18 or 19 (react satisfies ^18.0.0 || ^19.0.0). The Vite plugin alone does not require React for Vue, Svelte, or Blade projects.
After installing the package, run the init helper from your app root (where package.json and vite.config.* live). It patches Vite, Tailwind (v3 or v4), and VS Code settings when it can do so safely.
npx vite-plugin-useclassy initOptions:
# React: include className:* IntelliSense patterns
npx vite-plugin-useclassy init --language react
# Svelte
npx vite-plugin-useclassy init --language svelteAdd --dry-run to any init command to print the planned file changes without modifying your repo (for example npx vite-plugin-useclassy init --language react --dry-run).
If detection fails or your config is non-standard, use the manual steps below.
Add useClassy to your Vite plugins. It's recommended that you place it before Tailwind or other CSS processing plugins.
// vite.config.ts
import useClassy from "vite-plugin-useclassy";
export default {
plugins: [
useClassy({
language: "react", // or 'vue', 'blade', or 'svelte'
// Optional: Customize the output directory. Defaults to '.classy'.
// outputDir: '.classy',
// Optional: Customize output file name. Defaults to 'output.classy.html'.
// outputFileName: 'generated-classes.html'
// Optional: Enable debugging. Defaults to false.
// debug: true,
}),
// ... other plugins
],
};// Input (using class:variant attributes)
<button
className="px-4 py-2 rounded bg-blue-600 text-white"
className:hover="bg-blue-700 scale-105"
className:focus="ring-2 ring-blue-300"
className:disabled="opacity-50 cursor-not-allowed"
className:dark="bg-sky-700"
/>
// Output (after transformation by the plugin)
<button
className="px-4 py-2 rounded bg-blue-600 text-white hover:bg-blue-700 hover:scale-105 focus:ring-2 focus:ring-blue-300 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-sky-700 dark:hover:bg-sky-800"
/><template>
<button
class="px-4 py-2 rounded bg-blue-600 text-white"
class:hover="bg-blue-700 scale-105"
class:focus="ring-2 ring-blue-300"
class:disabled="opacity-50 cursor-not-allowed"
class:dark="bg-sky-700"
/>
<!-- Output -->
<button
class="px-4 py-2 rounded bg-blue-600 text-white hover:bg-blue-700 hover:scale-105 focus:ring-2 focus:ring-blue-300 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-sky-700 dark:hover:bg-sky-800"
/>
</template>Set language: "svelte" so the plugin uses Svelte-aware regexes. Place useClassy before @sveltejs/vite-plugin-svelte so UseClassy modifiers are rewritten before the Svelte compiler sees them.
useClassy({
language: "svelte",
});<button
class="px-4 py-2 rounded bg-blue-600 text-white"
class:hover="bg-blue-700 scale-105"
class:focus="ring-2 ring-blue-300"
class:active={isActive}
>
Click
</button>
<!-- Output -->
<button
class="px-4 py-2 rounded bg-blue-600 text-white hover:bg-blue-700 hover:scale-105 focus:ring-2 focus:ring-blue-300"
class:active={isActive}
>
Click
</button>Quoted UseClassy modifiers (class:hover="...") are transformed. Native Svelte class directives (class:active={isActive} or shorthand class:active) are left unchanged.
For Laravel applications, install the dedicated Composer package. The service provider will be automatically registered via Laravel's package auto-discovery.
composer require useclassy/laraveluseClassy({
language: "blade",
});<h1 class="text-xl" class:lg="text-3xl" class:hover="text-blue-600">
Responsive heading that changes on large screens and hover
</h1>The package transforms these during Blade compilation:
class:lg="text-3xl"becomeslg:text-3xlclass:hover="text-blue-600"becomeshover:text-blue-600class:dark="bg-gray-800 text-white"becomesdark:bg-gray-800 dark:text-white
These transformed classes are merged with any existing class attributes.
- PHP ^8.1
- Laravel ^10.0|^11.0|^12.0
UseClassy writes discovered classes to .classy/output.classy.html by default (configurable via outputDir / outputFileName). Tailwind must scan that file so utilities like hover:… exist in CSS.
The plugin adds .classy/ to .gitignore. Tailwind CSS v4 does not scan gitignored paths during automatic detection, so it will miss the manifest unless you register it explicitly. Use @source in your CSS (v4) or add the file to content (v3). See Tailwind’s docs: Detecting classes in source files.
@source paths are relative to the stylesheet file, not necessarily the project root. If your entry CSS lives in src/, the line may look like ../.classy/output.classy.html instead of ./.classy/....
/* Example when the stylesheet is next to package.json */
@import "tailwindcss";
@source "./.classy/output.classy.html";Add the manifest to content in tailwind.config.*:
export default {
content: [
// ...existing paths
"./.classy/output.classy.html",
],
};The package exports stable defaults and helpers so docs, init, and your own scripts stay aligned:
import {
getUseClassyTailwindSourceDirective,
getUseClassyTailwindSourceLineForRootStylesheet,
getUseClassyTailwindV3ContentEntry,
} from "vite-plugin-useclassy";
// or: import { ... } from "vite-plugin-useclassy/tailwind";
// v4: correct @source for a given CSS file path
const line = getUseClassyTailwindSourceDirective(
"/path/to/project/src/app.css",
"/path/to/project",
);
// line → @source "../.classy/output.classy.html";
// v4 shorthand when the CSS file sits beside package.json:
getUseClassyTailwindSourceLineForRootStylesheet();
// → @source "./.classy/output.classy.html";
// v3 content array entry (default output paths):
getUseClassyTailwindV3ContentEntry();
// → "./.classy/output.classy.html"If you customize outputDir or outputFileName in useClassy({ ... }), pass the same options into these helpers.
Add the following to your editor settings to enable IntelliSense for UseClassy variant attributes.
{
"tailwindCSS.classAttributes": [
"class",
"class:[\\w:-]*",
"className",
"className:[\\w:-]*"
]
}For Vue-only projects you can omit the className entries. Running npx vite-plugin-useclassy init merges these into .vscode/settings.json when possible.
Use this prompt in your editor agent when you want a one-shot manual setup (for example if init cannot patch your repo):
Prompt — “Set up UseClassy in this repo”
- Install dev dependency:
vite-plugin-useclassy(use the repo’s package manager: npm, pnpm, or yarn). - Open
vite.config.*. Addimport useClassy from 'vite-plugin-useclassy'. Inplugins, insertuseClassy({ language: '<vue|react|blade|svelte>' })before@tailwindcss/viteor other CSS pipeline plugins so it runs early. - Tailwind v4 (project uses
@import "tailwindcss"and typically@tailwindcss/vite): In the main CSS entry that imports Tailwind, add an@sourceline pointing at the generated manifest. Default manifest path is.classy/output.classy.htmlfrom the project root; the@sourcepath must be relative to that CSS file. IfuseClassyuses customoutputDir/outputFileName, use those instead. - Tailwind v3 (
tailwind.config.*): Add".classy/output.classy.html"(or./.classy/output.classy.htmlas appropriate) to thecontentarray without removing existing entries. - VS Code: In
.vscode/settings.json(merge, do not wipe), set or extendtailwindCSS.classAttributesto include"class:[\\w:-]*". For React, also add"className:[\\w:-]*". - Run
devonce so.classy/output.classy.htmlis generated; confirm Tailwind includes a class that only appears on aclass:hoverorclassName:hoverattribute.
A Cursor rule template you can copy into an app repo lives at templates/useclassy-setup.cursor-rule.mdc.
Enable debugging by setting debug: true in the plugin options. This will log detailed information about the plugin's operation to the console.
useClassy({
debug: true,
});- Only processes files with
.vue,.tsx,.jsx,.html,.blade.phpextensions. - Does not process files in the
node_modulesdirectory. - Does not process files in
.gitignoredirectories. - Does not process virtual modules.
Contributions are welcome! Please open an issue or submit a pull request.
MIT