Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

How to use Tailwind 4 programatically in JavaScript/TypeScript? #16581

Unanswered
MickL asked this question in Help
Discussion options

I am trying to use Tailwind 4 in my backend code to create dynamic results. Unfortunately I cant make it work, this is what I go so far:

import tailwindcss from '@tailwindcss/postcss';
import autoprefixer from 'autoprefixer';
const postcssResult = await postcss([
 tailwindcss({
 content: [{ raw: "<div class='text-red-500 font-bold'>Hello</div>" }]
 }),
 autoprefixer(),
 ]).process(`@import "tailwindcss"`, {
 from: undefined,
 });
 return postcssResult.css;

For content it tells me content does not exist in type PluginOptions but the bigger problem is it throws an error because it cant find the css @import "tailwindcss":

 Can't resolve 'tailwindcss'

Stackblitz: https://stackblitz.com/edit/github-u6vrxc-dfzdtw4o?file=routes%2Findex.ts

You must be logged in to vote

Replies: 5 comments 9 replies

Comment options

You may need to set base and from properly, see #16097 (comment).

However, @tailwindcss/postcss is file-system based which means you would need to create a temporary file for the raw content and have Tailwind scan that. Otherwise, you could try to adapt the code in @tailwindcss/vite for your needs (since that does not run Tailwind in a file-based way).

You must be logged in to vote
0 replies
Comment options

You can probably just use tailwindcss or maybe @tailwindcss/node if you are willing to mess around with the internal/undocumented/not public APIs we use for everything.

Some code to poke around at:

https://github.com/tailwindlabs/tailwindcss/blob/main/packages/%40tailwindcss-browser/src/index.ts#L102-L106

https://github.com/tailwindlabs/tailwindcss/blob/main/packages/%40tailwindcss-browser/src/index.ts#L205

You must be logged in to vote
2 replies
Comment options

Hi @adamwathan , did anything change since february? Is there maybe now a way to compile programmatically? Or maybe are this internal apis now documented?

Comment options

A typical use case I came across multiple times now: In my Backend (H3/Nitro) I want to render a Mail or PDF template as html. For this I can use Vue which renders the html as a string. For the styles I would love to use Tailwind, but it would require that I have a function (that hopefully works not just in Node, but also other environments like serverless, Deno, Bun, etc) and accepts and input (either the imported vue file or the generated html string) without use of the filesystem. This function would return the styles as a string, optionally minified etc.

With Tailwind 3 this was possible, but with Tailwind 4 I cant find the right config. I assume using post-css is the key again but I couldt make it ...

Comment options

Hey @MickL, did you find a solution? I'm looking for the same thing after upgrading to v4. Thanks!

You must be logged in to vote
6 replies
Comment options

Did you find out anything?

Comment options

I found a solution, I hope it works for you too.

You have to use something like jsdom or parse5 to parse your html and extract all classnames, and end with an array like this:
["bg-gray-50","p-6","rounded-lg","shadow-md","text-center","bg-[#f29101]","flex","h-16","items-center","justify-center","mb-4","mx-auto","rounded-full","w-16","h-8","text-black","w-8","font-semibold","mb-2","text-gray-900","text-xl","text-gray-600"]

Then you just need to install tailwindcss dependency, no need for postcss, and build a compiler like this:

import { compile } from "tailwindcss";
const tailwindCompiler = await compile('@import "tailwindcss";', {
 loadStylesheet: async (id: string, base: string) => {
 if (id === "tailwindcss") {
 return {
 path: "virtual:tailwindcss/index.css",
 base,
 // try to find more elegant ways of loading tailwind index.css depending on your bundler
 content: await readFile(
 "node_modules/tailwindcss/index.css",
 "utf-8",
 ),
 };
 }
 throw new Error(`can't load stylesheet id:${id} base:${base}`);
 },
});

Then you compile passing our previous list of classes, like this:

tailwindCompiler.build(["bg-gray-50","p-6","rounded-lg","shadow-md","text-center","bg-[#f29101]","flex","h-16","items-center","justify-center","mb-4","mx-auto","rounded-full","w-16","h-8","text-black","w-8","font-semibold","mb-2","text-gray-900","text-xl","text-gray-600"])

And thats it 😄

Comment options

Thanks this might be something! Just wanted to add that postcss is needed to run autoprefixer and minification after tailwind

Comment options

I think autoprefixer is not longer needed in v4

Comment options

Why max autopefixer no longer needed?
I found react-email using Tailwind 4 and they also use the compile() function. But what they do is they scan all of their dom elements and then add all used classes manually. Posted the links below in another answer.

Comment options

I solved this by adding the following line to my global css file

@source "../src/**/*.{astro,html,js,jsx,ts,tsx}"
You must be logged in to vote
1 reply
Comment options

How does this help in a JavasScript file?

Comment options

@adamwathan Did anything change over the course of this year? May we have capabilities to use Tailwind programmatically now or is it better documented?

This is my current solution, it works:

import { compile } from 'tailwindcss';
import tailwindStyles from 'tailwindcss/index.css?raw';
let compiler: { build(candidates: string[]): string };
export async function useTailwind(html: string, styles = '@import "tailwindcss";'): Promise<string> {
 if (!compiler) {
 compiler = await compile(styles, {
 loadStylesheet: async (id: string, base: string) => {
 if (id === 'tailwindcss') {
 return {
 path: 'virtual:tailwindcss/index.css',
 base,
 content: tailwindStyles,
 };
 }
 throw new Error(`can't load stylesheet id:${id} base:${base}`);
 },
 });
 }
 const classes = Array.from(html.matchAll(/class\s*=\s*("|')(.*?)1円/g), (m) => m[2])
 .flatMap((s) => s.split(/\s+/))
 .filter(Boolean);
 return compiler.build(classes);
}
You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /