How to use Satori with your Tailwind config
A quick guide to using Satori with your Tailwind plugins, fonts, and everything else in your config!
Introduction
Satori is an easy to use library that lets you generate an SVG file using React (or Preact)! In my opinion, it’s the nicest way to generate images.
Satori also comes with Tailwind support by default, but there’s a catch - it doesn’t work with your Tailwind config out-of-the-box. In this blog post, I’ll be showing you how to get Satori to work with your Tailwind config!
Installing Dependencies
We’ll first need to download the dependencies we need. I’ll be using Preact in this tutorial, but React works too (albeit with a few minor changes in the imports and types)
npm install preact # or `npm install react`
npm install satori
We’ll also need the tw-to-css
library, which’ll convert our Tailwind classes into a style
prop that Satori can render.
npm install tw-to-css
The Code
First, let’s import the things we’ll be using:
import { tailwindToCSS, type TailwindConfig } from "tw-to-css";
import { cloneElement, isValidElement, type h } from "preact";
import { Children } from "preact/compat";
We then need to get a twj
function (Tailwind to JSON); we’ll use it to convert our Tailwind classes into an object of inline styles that the style
prop will use:
const { twj } = tailwindToCSS({
config: (await import("../tailwind.config.mjs")).default as TailwindConfig,
});
Make sure to replace the path above with the path to your Tailwind config!
Now, let’s define an inlineTailwind
function that’ll convert the Tailwind classes into inline styles:
function inlineTailwind(el: h.JSX.Element): h.JSX.Element {
const { tw, children, style: originalStyle, ...props } = el.props;
// Generate style from the `tw` prop
const twStyle = tw ? twj(tw.split(" ")) : {};
// Merge original and generated styles
const mergedStyle = { ...originalStyle, ...twStyle };
// Recursively process children
const processedChildren = Children.map(children, (child) =>
isValidElement(child) ? inlineTailwind(child as h.JSX.Element) : child,
);
// Return cloned element with updated props
return cloneElement(el, { ...props, style: mergedStyle }, processedChildren);
}
And you’re done! Here’s an example of how you can use this function:
function Component() {
return <div tw="flex bg-base">Hi there! 👋</div>;
}
const element = Component();
const jsx = inlineTailwind(element);
const svg = await satori(jsx, {
width: 1200,
height: 630,
// ... any other satori options
});
Thanks to this issue on GitHub for parts of the code!