Skip to content

Instantly share code, notes, and snippets.

@SystemDisc
Created July 23, 2024 07:52
Show Gist options
  • Save SystemDisc/af1565ef2cc3268e704b5cec054301e1 to your computer and use it in GitHub Desktop.
Save SystemDisc/af1565ef2cc3268e704b5cec054301e1 to your computer and use it in GitHub Desktop.
Get Tailwind CSS styles for a dynamic HTML string (e.g. CMS content) in a Next.js 14+ Server Component
import appRootPath from 'app-root-path';
import { readFile } from 'fs/promises';
import { resolve } from 'path';
import postcss from 'postcss';
import tailwindcss, { Config } from 'tailwindcss';
function extractTailwindClasses(htmlContent: string): string[] {
const classRegex = /class=("([^"]+?)"|'([^']+?)')/g;
const classes = new Set<string>();
let match;
while ((match = classRegex.exec(htmlContent)) !== null) {
(match[2] || match[3])
.split(' ')
.forEach((className) => classes.add(className));
}
return Array.from(classes);
}
export default async function Page() {
// This will obviously come from the DB or API call or whatever
const html = String.raw`
<header class="bg-blue-600 text-white p-6">
<div class="container mx-auto text-center">
<h1 class="text-3xl font-bold">Lorem Ipsum</h1>
<p class="text-lg">Dolor Sit Amet</p>
</div>
</header>
<main class="container mx-auto p-6">
<section class="mb-8">
<div class="bg-white shadow-md rounded-lg overflow-hidden">
<div class="p-6">
<h2 class="text-2xl font-semibold mb-4">About Me</h2>
<p class="mb-4">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce euismod felis ac neque semper, at vestibulum nunc blandit. Curabitur non nibh ut justo posuere luctus a id turpis.
</p>
<p>
Aenean sit amet elit sit amet velit ultricies pulvinar. Nulla facilisi. Duis sollicitudin magna vel arcu ullamcorper, eget tincidunt tortor malesuada.
</p>
</div>
</div>
</section>
<section>
<div class="bg-white shadow-md rounded-lg overflow-hidden">
<div class="p-6">
<h3 class="text-xl font-bold mb-4">Contact Information</h3>
<p class="mb-4"><strong>Email:</strong> [email protected]</p>
<p class="mb-4"><strong>Phone:</strong> (123) 456-7890</p>
<p class="mb-4"><strong>Location:</strong> Lorem City, Ipsum State</p>
</div>
</div>
</section>
</main>
<footer class="bg-gray-800 text-white p-6 mt-8">
<div class="container mx-auto text-center">
<p>&copy; 2024 Lorem Ipsum. All rights reserved.</p>
</div>
</footer>
`;
const tailwindClasses = extractTailwindClasses(html);
const tailwindConfigPath = resolve(appRootPath.path, 'tailwind.config.js');
let tailwindConfig: Config = {
content: [],
};
const configJs = (await readFile(tailwindConfigPath))
.toString()
.replace(/module\.exports\s*=/m, 'tailwindConfig =');
eval(configJs); // only safe because we own this file - don't let anything silly happen
tailwindConfig.content = [];
tailwindConfig.safelist = tailwindClasses;
const { css } = await postcss([tailwindcss(tailwindConfig)]).process(
'@tailwind utilities;'
);
return (
<>
<style type="text/css" dangerouslySetInnerHTML={{ __html: css }} />
<div dangerouslySetInnerHTML={{ __html: html }} />
</>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment