Skip to main content
If you have an existing site with content baked into the code — blog posts in a JSX file, testimonials in a hardcoded array, product data in a sprawling JSON blob — you can move that content into the CMS and let the editing UI take over. Once that’s done, content updates don’t need a developer or an AI prompt; anyone with editor access can change them in the CMS panel. This guide walks through the migration end-to-end and includes ready-to-use prompts for several content types.
This is the canonical workflow when your site wasn’t designed with the Hiveku CMS in mind. The AI does the heavy lifting; your job is to describe the source and verify the result.

The Idea

Most React sites grow by accretion. Someone adds a testimonials.tsx with six hardcoded testimonials. A blog ships with posts in posts.json. Products live in lib/products.ts as a TypeScript array. The migration replaces those embedded sources with CMS-managed entries:
  • The data moves from JSX/TS arrays to one file per item under content/
  • The manifest (hiveku.cms.json) gains a collection per content type
  • The React components stop hardcoding data and start reading the CMS
  • Non-developers gain a Webflow-style editor for that content

The Flow

1

Open the AI chat

Click the AI tab in /v3. You’ll be doing the migration through plain-English prompts.
2

Describe what you have

Be specific about where the content lives:
I have a blog with 12 posts hardcoded in app/blog/page.tsx. Pull them
into a CMS-managed blog collection.
Or for multiple content types:
There are testimonials hardcoded in components/Testimonials.tsx,
products in lib/products.ts, and FAQ entries in app/help/page.tsx.
Migrate them all to CMS collections, one at a time.
3

Let the AI propose a schema

The AI reads the source file, infers the shape (title, date, body, etc.), and proposes a manifest entry for the new collection. It also surfaces ambiguity:“I see most posts have a date field but two have publishedDate instead — should I normalize to publishedAt?”Confirm or adjust before it writes anything.
4

AI scaffolds the manifest

If the project has no hiveku.cms.json yet, the AI creates it. Otherwise it adds the new collection to the existing manifest. Either way, you’ll see a diff before it saves.
5

AI writes the entries

One file per item — MDX for blog-like content, JSON for structured data. The AI uses the content’s existing slug if there is one, or generates a sensible slug from the title.For 12 blog posts you’ll see 12 new files under content/blog/.
6

AI refactors the React component

The component that used to hardcode the data now reads from the CMS. For Next.js App Router, that usually means importing a CMS helper, fetching entries at build/request time, and rendering them.The AI shows a diff: array literal removed, CMS read added, types updated. Approve and save.
7

Verify in /v3

Open the CMS panel, switch to the new collection. You should see all 12 entries. Open one, edit a field, save — the live preview updates.
8

Repeat for other content types

Do one collection at a time. After blog, do testimonials. Then products. Then FAQs. Each one is a separate prompt-confirm-verify cycle.
9

Hand off to the content team

Once components read from the CMS, content updates no longer need code changes. The content team uses the CMS panel; developers don’t get pinged for typo fixes.

Examples by Content Type

Blog → MDX collection

Source: app/blog/page.tsx with an array of posts. Prompt:
I have 12 hardcoded blog posts in app/blog/page.tsx. Each has a title,
date, body (Markdown), and tags. Move them into a "blog" CMS collection
in MDX format, then refactor the page to read from the CMS. The route
pattern should be /blog/{slug}.
What you’ll get:
  • A blog collection in hiveku.cms.json with title, publishedAt, tags, body
  • 12 MDX files in content/blog/ — one per post, with frontmatter for metadata and Markdown body
  • A rewritten app/blog/page.tsx that lists posts from the CMS
  • (Usually) a new app/blog/[slug]/page.tsx if individual post routes weren’t already wired up

Testimonials → JSON collection

Source: components/Testimonials.tsx with hardcoded objects. Prompt:
There are 6 testimonials in components/Testimonials.tsx with quote, name,
company, and avatar URL. Move them to a "testimonials" CMS collection
(JSON format) and refactor the component to read from it.
What you’ll get:
  • A testimonials collection with name, company, quote, avatar, plus a useful featured boolean
  • 6 JSON files in content/testimonials/
  • A rewritten <Testimonials /> component that reads the collection

Product catalog → JSON collection with references

Source: lib/products.ts with a TypeScript array, including category strings on each product. Prompt:
I have ~40 products in lib/products.ts with name, sku, price, currency,
images, description, and a category string. Move them to a "products"
CMS collection. Pull the unique categories into their own "categories"
collection. Make the category field on products a reference to a category.
Refactor app/products/page.tsx and app/products/[slug]/page.tsx.
What you’ll get:
  • A categories collection with name, slug, description
  • A products collection with name, sku, price, currency, images, description, and category (reference)
  • 40 product files + N category files
  • Rewritten product list and detail pages
  • The category filter UI rebuilt to read categories from the CMS too

FAQs → JSON collection

Source: app/help/page.tsx with question/answer pairs grouped by category. Prompt:
The FAQ content in app/help/page.tsx is grouped by category — Billing,
Technical, General. Move all questions to an "faqs" CMS collection with
question, answer, and category fields. Refactor the help page to read
the collection and group by category.
What you’ll get:
  • An faqs collection with question, answer, category (select), and order
  • One JSON file per question
  • A rewritten help page that reads, sorts, and groups from the CMS

Tips for a Smooth Migration

Do one collection at a time. Migrating blog, testimonials, products, and FAQs in one prompt is technically possible but harder to verify. Sequential prompts produce cleaner diffs and let you spot issues before they compound.
Snapshot your project first. Use the dashboard’s snapshot feature before each migration so you can roll back the whole project if a refactor goes sideways.
Keep one entry as a reference. Before starting a migration, copy one entry’s hardcoded data somewhere safe. After the migration, you can spot-check the corresponding CMS entry to make sure nothing was lost in translation.

What “Read From the CMS” Means

After migration, your component fetches entries from the CMS at build or request time. The exact code depends on your framework, but the shape is usually:
// app/blog/page.tsx (Next.js App Router, after migration)
import { listEntries } from '@/lib/cms';

export default async function BlogIndexPage() {
  const posts = await listEntries('blog', {
    where: { status: 'published' },
    sort: 'publishedAt desc',
  });

  return (
    <div>
      {posts.map((post) => (
        <article key={post.slug}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  );
}
The AI will scaffold the helper (@/lib/cms) the first time it’s needed and reuse it for subsequent migrations.

After the Migration

A few things to set up once content is in the CMS:
  • Invite the content team. Add editors to the project from the dashboard. They get access to the CMS panel without code editor access.
  • Set up status filters. If you added a status field, content goes through a draft → published flow. Make sure the React component filters out drafts in production.
  • Tell the team about the CMS panel. A 5-minute walkthrough of /v3 → CMS mode → form editor goes a long way.
Once your components read from the CMS, deleting an entry removes that content from the live site. Use the status: draft workflow if you want to take entries out of the public view without deleting them.

Troubleshooting

Tell it: “You missed the excerpt field that’s on every post — add it to the schema.” The AI will revise the manifest and re-process the entries.
If body content was HTML and got copied as-is into a Markdown field, the formatting won’t render. Ask the AI: “Convert all blog post bodies from HTML to Markdown.”
Most often the component is filtering for status: published but no entries have that status set. Check one entry in the CMS panel — if status is unset, set the default in the manifest and ask the AI to backfill: “Set status to ‘published’ for all blog entries that don’t have it.”
If two source posts had the same title or slug, the AI auto-disambiguates with -2, -3 suffixes. To clean up, rename the entries one at a time in the CMS panel — Hiveku updates internal links automatically.
The migration adds entries but doesn’t always wire up the dynamic route. Ask: “My /blog/ pages are 404 — make sure the dynamic route reads the CMS by slug.”
They might be in code editor mode, not CMS mode. Walk them through opening /v3 → right pane → CMS tab. If a field doesn’t appear in the form, it’s missing from the manifest — add it via Collection Manager.

What’s Next?

Add a Collection

Add more content types after the initial migration

References

Link collections together, like blog posts to authors

Bulk Import

Bring in a large CSV/JSON dump in one shot

AI Integration

Prompt patterns for content tasks