Troubleshooting

Content renders wrong or unstyled

Updated 92 views

Rendering problems fall into a few recognizable shapes. For the ready-made React components, such as BlogPostList or HelpArticle, see Fixing problems with the React components.

Raw HTML tags on the page

If readers see literal <p> and <strong> text, the HTML is being escaped rather than rendered.

Frameworks escape strings by default — that is a security feature. Rendering HTML requires opting in explicitly:

tsx
// React — wrong
<div>{post.contentHtml}</div>

// React — right
<EntryContent entry={post} /> // from @kookee/react/entry

// or without the package (memoise the object in a client component)
<div className="kookee-entry-content" dangerouslySetInnerHTML={{ __html: post.contentHtml }} />
vue
<!-- Vue — wrong -->
<div>{{ post.contentHtml }}</div>

<!-- Vue — right -->
<div class="kookee-entry-content" v-html="post.contentHtml" />

This is safe for Kookee content because it comes from your own dashboard and authors — the same trust level as your application code. Do not route user-submitted content through the same path without sanitizing it.

contentHtml is undefined

tsx
<div className="kookee-entry-content" dangerouslySetInnerHTML={{ __html: post.contentHtml }} />
// TypeError: Cannot read properties of undefined

You are almost certainly rendering a list item. List responses omit the body:

TypeScript
const { data } = await kookee.blog.list();
data[0].contentHtml; // undefined by design

const post = await kookee.blog.getBySlug(data[0].slug);
post.contentHtml;    // present

For a preview on an index page, use excerptHtml, which list items do include.

Code blocks have no styling

Dark backgrounds and syntax colors come from a stylesheet that ships with the SDK but is not imported automatically:

TypeScript
// Entry file — main.ts, App.tsx, or app/layout.tsx
import '@kookee/sdk/styles/content.css';

Or over a CDN:

HTML
<link rel="stylesheet" href="https://unpkg.com/@kookee/sdk/styles/content.css">

The SDK ships two stylesheets and no others: content.css, which carries the code-block and file-chip rules, and typography.css for headings, lists and paragraphs. Skip the second one if your own typography already styles the body.

Import it once, at the application root. Importing per component works but duplicates the CSS.

The stylesheet is scoped to the kookee-entry-content class, so the element that holds the HTML needs it. EntryContent from @kookee/react adds it; a hand-written dangerouslySetInnerHTML or v-html needs the class added.

Typography looks broken

Content HTML is unstyled semantic markup. Many CSS resets strip default heading and list styles, so headings render at body size and lists lose their bullets.

Tailwind Typography is the simplest fix:

tsx
<article className="prose prose-lg max-w-none">
  <div className="kookee-entry-content" dangerouslySetInnerHTML={{ __html: post.contentHtml }} />
</article>

max-w-none matters — prose caps width by default, which usually fights your own layout.

Without Tailwind, scope styles to a wrapper class:

CSS
.kookee-content h2 { font-size: 1.5rem; margin-top: 2rem; }
.kookee-content p  { margin-bottom: 1rem; line-height: 1.7; }
.kookee-content ul { list-style: disc; padding-left: 1.5rem; }

Tailwind prose fighting the code styles

The SDK stylesheet has no !important rules. In Tailwind v4, import it into a cascade layer, so your prose-code: utilities win over its defaults:

CSS
@import '@kookee/sdk/styles/content.css' layer(components);

Then add this unlayered guard once, so prose's own pre styles do not reach inside code blocks:

CSS
.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;
}

To change the code colours themselves, set the stylesheet's --kookee-code-* variables, such as --kookee-code-inline-bg, rather than overriding its selectors.

The chat widget is unstyled

The React KookeeChatWidget needs its own stylesheet, separate from the SDK's:

tsx
import { KookeeChatWidget } from '@kookee/react/chat';
import '@kookee/react/chat.css'; // easy to miss

Without it the widget renders as unstyled markup. The script-tag widget has no stylesheet to import — it brings its own.

Your CSS does not affect the chat widget

For the script-tag widget this is by design, not a bug. It renders inside a shadow root: your page's styles cannot reach into it, and its styles cannot leak onto your page. CSS variables and selectors written against it will do nothing.

The React KookeeChatWidget is different: it renders in your page and reads the same --kookee-* variables as the React components, so variables you set on :root for those components reach it too.

Restyle the script-tag widget with its documented attributes instead, data-theme and data-primary-color. In React, use the theme and primaryColor props, and the documented --kookee-* variables as well. See Customize the chat widget's appearance.

The chat widget does not follow my site's dark mode

data-theme="auto" follows the visitor's operating system, not your site. If your site has its own theme toggle, tell the widget when it changes: call KookeeChat.setTheme('dark') — or 'light', 'auto' — from the toggle, or pass your current theme to the theme prop in React. On the script-tag widget, overriding its colors with CSS will not work, for the reason above. See Customize the chat widget's appearance.

The chat widget answers nothing useful

The widget answers from your published help center articles only. If an article is still a draft, it is not available to the chat.

A published article is found once it is indexed for AI search, which uses your plan's monthly AI embedding tokens. When those are used up, the chat keeps answering, but new articles are not indexed and changed ones keep their old text in search, and the article's sidebar keeps showing Indexing in progress.... Save the article again after the tokens reset on the 1st of the month, or move to a larger plan. The AI Embedding Tokens card on the billing tab shows how many are left.

When the month's AI answers are used up, the chat stops answering until the 1st.

The chat widget errors in Next.js

From @kookee/react 1.9.0, @kookee/react/chat is marked 'use client', so a Server Component can render KookeeChatWidget directly. The error that remains is Functions cannot be passed directly to Client Components, when a Server Component passes it a function such as onSourceClick. On older versions, or to pass a function, wrap it:

tsx
'use client';

import { KookeeChatWidget } from '@kookee/react/chat';
import '@kookee/react/chat.css';

export function ChatWidget() {
  return <KookeeChatWidget clientOptions={{ apiKey: process.env.NEXT_PUBLIC_KOOKEE_API_KEY! }} />;
}

A stylesheet import fails to resolve

An error like Package subpath './styles.css' is not defined by "exports" means the path does not exist. Each package exports a fixed set:

  • @kookee/react — @kookee/react, @kookee/react/chat, @kookee/react/chat.css, and from 1.9.0 the components: @kookee/react/entry, /entry/client, /blog, /changelog, /changelog/client, /help, /help/client, /roadmap and @kookee/react/content.css

  • @kookee/sdk — @kookee/sdk, @kookee/sdk/consent, @kookee/sdk/styles/content.css, @kookee/sdk/styles/typography.css

Anything else throws at build time. The two easiest to get wrong are @kookee/react/styles.css, which does not exist and should be @kookee/react/chat.css for the chat widget or @kookee/react/content.css for the components, and @kookee/sdk/styles/code.css, which should be @kookee/sdk/styles/content.css.

Images or attachments not loading

Images in content are stored as URLs in the HTML and load directly. If they are blocked, check your Content Security Policy — a strict img-src will reject them until the host is allowed.

Layout breaks on mobile

Content HTML includes tables and code blocks that can exceed narrow viewports. Let them scroll inside their own container rather than pushing the page wide:

CSS
.kookee-content pre,
.kookee-content table {
  overflow-x: auto;
  max-width: 100%;
}

.kookee-content img {
  max-width: 100%;
  height: auto;
}

Was this article helpful?