This file is the single source of truth for what to do next to get new.naturesboon.net production‑ready, especially for blogs and Puck.
Goal: No JSON blobs / FAQ schema junk in the visible article body, and clean typography.
- Renderer already updated
app/blogs/[slug]/BlogPostClient.tsx- Detects Puck JSON vs legacy HTML vs markdown.
- Cleans HTML and strips most schema/FAQ JSON fragments.
- Uses
PuckRendererfor Puck posts, otherwise uses HTML/markdown.
- Rich text editor upgraded
components/puck/RichText.tsx- Inline editor with bold/italic/underline, bullets, numbered lists, headings (H2–H4), quotes, divider, links (add/remove), clear formatting.
- Sanitizes pasted HTML (removes
<script>,<style>, inline events, obvious schema JSON).
- Immediate manual cleanup for key posts
- For top SEO blogs (start with 3–5):
- Go to
/admin/blogs. - Edit the post, convert body into 2–4
TextBlocks (use headings + spacing). - Remove any leftover raw schema text in the editor.
- Use
InlineImageblocks for key visuals. - Publish.
- Go to
- For top SEO blogs (start with 3–5):
Goal: Blogs are structured as sections + FAQs, not one giant blob.
-
Ensure
TextBlock+InlineImageare registered for blogscomponents/puck/blog-config.tsx- Components:
TextBlock,InlineImage,Spacer. - Category
"Blog Content"exposes them in the Puck sidebar.
- Components:
-
Add
FAQBlock(structured FAQs)- Create
components/puck/blocks/FAQBlock.tsxwith:- Fields:
title: stringintro: stringitems[]withquestion: string,answer: string
- Render:
- Wrap in
Section. - Show title/intro.
- List of
<details><summary>Q</summary><div>A</div></details>for accordions.
- Wrap in
- Fields:
- Register in
components/puck/config.tsxANDcomponents/puck/blog-config.tsx:categories.Blog.componentsinclude"FAQ".components.FAQ = FAQBlockConfig.
- Create
-
Optional later blocks
ReviewListBlock— structured list of Trustindex / review excerpts.HighlightBlock— short “Key Takeaways” at the end of a blog.
Goal: Automatically convert scraped WordPress/SEO content into structured Puck layouts where possible.
High‑level steps:
-
Write a migration script (Node or Convex function)
- Input: all docs from
blogstable. - Skip any blog where
content.trim().startsWith("{")(already Puck JSON). - For others:
- Treat
blog.contentas HTML / text.
- Treat
- Input: all docs from
-
Extract FAQ schema from content
- Look for trailing JSON starting with
{"@context":"https://schema.org","@type":"FAQPage",...}. - Regex idea:
/\{\s*"@context"\s*:\s*"https?:\\\/\\\/schema\.org"[\s\S]*?"@type"\s*:\s*"FAQPage"[\s\S]*?\}\s*$/i
- Parse JSON (fix escaping
\\\/→/). - Build
FAQBlockProps.items:items = faq.mainEntity.map(q => ({ question: q.name, answer: q.acceptedAnswer?.text }))
- Remove that JSON chunk from the body string.
- Look for trailing JSON starting with
-
Clean remaining HTML for
TextBlock- Reuse a “HTML cleaner” similar to
cleanHtmlContentinBlogPostClient:- Remove
<script>/<style>+ inline events. - Normalize entities (
, non‑breaking spaces). - Do NOT strip structural tags (
<p>, <h2>, <ul>, <ol>, <li>, <strong>, <em>, <a>).
- Remove
- Reuse a “HTML cleaner” similar to
-
Build Puck JSON layout per blog
- Minimal safe default:
const puckData = { content: [ { type: "TextBlock", props: { id: "body", content: cleanedHtml, alignment: "left", maxWidth: "md", }, }, faqItems.length ? { type: "FAQ", props: { id: "faq", title: "Frequently Asked Questions", items: faqItems, }, } : null, ].filter(Boolean), root: { props: {} }, };
- Save as
JSON.stringify(puckData)back toblogs.contentviaapi.blogs.updateBlog.
- Minimal safe default:
-
Dry‑run on 1–2 posts, then apply to all
- Pick a blog with visible FAQ JSON junk (e.g. derma manufacturers article).
- Run script only for that
_id, verify:- Body is readable.
- FAQ renders as accordions.
- Then run for all remaining legacy blogs.
File: app/blogs/[slug]/BlogPostClient.tsx
- If
contentparses as Puck JSON:- Use
PuckRendererwithblogConfig. - Pass
hideHeaderso only the outer page header shows. PuckRenderershould receivesiteSettingsand (optionally)initialDatafor things like product blocks.
- Use
- Else (legacy content):
- Detect HTML vs markdown.
- For HTML: sanitize and render with
dangerouslySetInnerHTMLin a styled container. - For plain text / markdown: run through
ReactMarkdownwith custom components.
-
Confirm editor UX
- In
/admin/blogs, create a new test blog. - Use
TextBlock+InlineImage+ (when added)FAQ. - Confirm inline editing, headings, lists, links work as expected.
- In
-
Implement
FAQBlock+ register it- Add file, wire into
config.tsxandblog-config.tsx.
- Add file, wire into
-
Manually rebuild 3–5 key SEO posts in Puck
- Use them as gold standards for migration shape.
-
Write and test a migration script
- Parse, extract FAQs, generate Puck JSON, update
blogs. - Test on 1–2 posts before bulk run.
- Parse, extract FAQs, generate Puck JSON, update
-
Later enhancement
- Add optional
ReviewListBlockif you want structured handling of long Trustindex review sections instead of raw paragraphs.
- Add optional
Keep this file updated as you iterate – it should always describe “what the next engineer should do next week if they pick this up cold.”