Kookee returns entry bodies as ready-to-render HTML in the contentHtml field. You do not need a Markdown parser — the conversion happens server-side.
Render the HTML
React: render it with EntryContent from @kookee/react:
import { EntryContent } from '@kookee/react/entry';
const post = await kookee.blog.getBySlug(slug);
<EntryContent entry={post} className="prose" />
It adds the kookee-entry-content class the stylesheets below are scoped to, works in React Server Components, and keeps the body's DOM when the parent re-renders. An inline dangerouslySetInnerHTML={{ __html }} does not: in React 19 it replaces the whole body on every render of the parent. Without the package, add the class yourself, and in a client component memoise the { __html } object:
const html = useMemo(() => ({ __html: post.contentHtml }), [post.contentHtml]);
<div className="kookee-entry-content" dangerouslySetInnerHTML={html} />
Vue:
<div class="kookee-entry-content" v-html="post.contentHtml" />
Both of these inject raw HTML, which is safe here because the content comes from your own dashboard and your own authors — the same trust level as your application code. Do not pipe user-submitted content through the same path without sanitizing it first.
Remember that contentHtml is only present on single-entry fetches. List items do not include it.
Import the code block stylesheet
The SDK ships CSS for code blocks:
// In your entry file — main.ts, App.tsx, or layout.tsx
import '@kookee/sdk/styles/content.css';
Or over a CDN:
<link rel="stylesheet" href="https://unpkg.com/@kookee/sdk/styles/content.css">
Without it, code blocks render as unstyled preformatted text.
Every rule in it is scoped to the kookee-entry-content class, so the element that holds the HTML needs that class. EntryContent adds it.
What the stylesheet provides
A theme inspired by VS Code Dark+, applied to these classes:
.kookee-code-block— the wrapper, with a dark background.kookee-code-block__header— language label and copy button row.kookee-code-block__language— the language label.kookee-code-block__copy— the copy button
Syntax highlighting uses highlight.js classes such as .hljs-keyword, .hljs-string, .hljs-comment, .hljs-title, .hljs-type, .hljs-property and .hljs-number.
Inline code outside of a block gets a distinct pink background so it stands out in prose.
The monospace stack is:
font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular,
Menlo, Monaco, 'Courier New', monospace;
With Tailwind Typography
<EntryContent entry={post} className="prose prose-lg max-w-none" />
In a Tailwind v4 app, import the stylesheet into a cascade layer, so your prose-code: utilities keep winning over its defaults:
@import '@kookee/sdk/styles/content.css' layer(components);
The layer cuts both ways: prose's own pre styles now also reach inside code blocks. Add this unlayered guard once:
.kookee-entry-content .kookee-code-block pre {
margin: 0;
border-radius: 0;
}
.kookee-entry-content .kookee-code-block pre code {
background: none;
padding: 0;
border: none;
border-radius: 0;
font-size: 0.875rem;
}
Custom overrides
The stylesheet reads custom properties, so theme it with variables rather than overriding its selectors:
:root {
--kookee-code-bg: #282c34;
--kookee-code-inline-bg: #e8f0fe;
--kookee-code-inline-fg: #1a73e8;
}
The code variables are --kookee-code-font, --kookee-code-bg, --kookee-code-header-bg, --kookee-code-inline-bg, --kookee-code-inline-fg and --kookee-code-inline-border. File chips read --kookee-file-chip-bg, --kookee-file-chip-border, --kookee-file-chip-fg, --kookee-file-chip-hover-bg and --kookee-file-chip-muted; in dark mode, set at least the chip background and border, whose defaults are light. The class names above are stable too, if you need to target an element directly.