Tailwind styles aren't loaded for Keystatic UI custom components in Astro
Hi!
I've been trying to setup a Footnote custom mark component, as written here. There's a sample repo using Next, but I had an issue getting the same result for Astro.
Basic idea is adding a custom component with tailwind classes:
// keystatic.config.tsx
// config.collections.<name>.schema
content: fields.markdoc({
label: "Text",
components: {
Footnote: mark({
label: "Footnote",
icon: <SuperscriptIcon />,
className: "align-super text-xs",
schema: {},
}),
},
}),
But when the text is marked in UI, styles aren't applied, because Keystatic Astro component has no idea about the styles file.
How I think this can be fixed
(some steps can be automated by keystatic integration, others might need user input)
- Add
"keystatic.config.tsx"to tailwind content config, so the custom component styles are picked up into global css - Add a custom Astro component, like so:
---
// src/components/MyKeystatic.astro
import Keystatic from '@keystatic/astro/internal/keystatic-astro-page.astro';
import "styles/globals.css";
export const prerender = false;
---
<div class="bg-blue-500 text-black">Custom Keystatic component with Tailwind</div>
<Keystatic />
- Expand integration with option to provide custom Astro component, like so:
// /node_modules/@keystatic/astro/dist/keystatic-astro.js
function keystatic(options) {
return {
name: 'keystatic',
hooks: {
'astro:config:setup': ({
...
const astroPage = options.astroPage ?? '@keystatic/astro/internal/keystatic-astro-page.astro'
logger.info(`Using ${astroPage} as Keystatic component`)
...
- Configure Astro integration:
// astro.config.mjs
integrations: [
markdoc(),
keystatic({ astroPage: "src/components/MyKeystatic.astro" }),
tailwind(),
]
As a result, tailwind CSS styles are loaded and applied to custom components in the editor UI.
If this approach makes sense — I'll gladly open a PR. Thanks!
Thought about this a bit more, found a workaround that could work without changes to Keystatic: By adding Astro middleware for keystatics routes, we can prepend the styles to the Astro island with Keystatic UI, by importing tailwind css content and injecting it into Astro styles. There might be a better way, but this works!
It's especially helpful for custom content components:
// keystatic.config.tsx
...
content: fields.markdoc({
label: "Body",
components: {
Badge: inline({
label: "Footnote",
schema: {
foo: fields.number({ label: "My Foo" }),
},
NodeView: ({ value }) => (
<Badge variant="secondary"> Foo: #{value.foo} </Badge>
),
})
Attaching the middleware code:
?url in style path is needed for vite to build for production, otherwise it produces
src/middleware.ts (2:7): "default" is not exported by "src/styles/globals.css", imported by "src/middleware.ts".
// /src/middleware.ts
import { defineMiddleware } from "astro:middleware";
import css from "./styles/globals.css?url";
export const onRequest = defineMiddleware(async (context, next) => {
const response = await next();
if (!context.url.pathname.startsWith("/keystatic")) {
return response;
}
const html = await response.text();
const redactedHtml = html.replace("<!DOCTYPE html>", `<!DOCTYPE html><meta charset="utf-8"/><link rel="stylesheet" href="${css}" />`);
return new Response(redactedHtml, {
status: 200,
headers: response.headers,
});
});
i think setting up the keystatic page and api route manually should also work, like here