Created
July 23, 2024 07:52
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>© 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