Integrations
Add a blog to a Lovable app
One prompt adds a blog to your Lovable app that copies your Kitful posts into the app's database and updates within seconds of every change.
Kitful gives you one prompt. Paste it into Lovable, and Lovable builds a blog inside your app on Lovable Cloud. Your posts are copied into the app's own database, so the blog loads as fast as any other page of the app, and a change in Kitful reaches it within seconds.
What the prompt builds
- A
kitful_poststable holding a copy of your posts. Visitors can read it; only the sync writes to it. - An edge function,
kitful-sync, that copies new, edited and deleted posts. Kitful calls it after every change, and a Lovable Cloud job runs it every hour in case a call is missed. /blog, newest posts first, and a page for each post with its title, description, canonical link, social preview tags and structured data, styled like the rest of your app.- A blog sitemap, listed in
robots.txt.
It works on both Lovable stacks: TanStack Start projects render the blog on the server, and React with Vite projects read the table in the browser.
Before you start
- Your domain on the app. The Blog URL must be on the same domain as your Kitful site. If your Kitful site is
example.com, connect that domain to your Lovable app, then usehttps://example.com/blog. An app with only alovable.appaddress works when your Kitful site uses that address. - Lovable Cloud. The prompt turns it on if it is off.
- No posts needed yet. The blog fills in as you publish.
Set it up
- Publish your Lovable app, so it has its public address.
- Connect a headless destination in Kitful with the Blog URL set to that address followed by
/blog. Leave the notification URL empty for now. - On the screen that shows the read token, pick Lovable under Set up your site and click Copy prompt. Keep that screen open: the token is shown only there.
- Paste the prompt into Lovable's chat. When Lovable asks for the read token, copy it from the Kitful screen and enter it in Lovable Cloud's secure secret input, never in the chat.
- Publish the app again. Lovable ends its answer with the notification URL, the address of
kitful-sync. - In Kitful, click Manage on the destination, paste that URL into Notification URL and click Save.
- Click Manage again and click Send test. Kitful reports Your site received the test notification.
- Publish an article to the destination and open
/blogon your app.
Good to know
- A new post appears within seconds of a publish, an edit within seconds of Update on site, and a deleted article leaves the blog on the next sync. The hourly job catches up after a missed notification.
- When it finishes, the prompt runs the sync once and Lovable tells you how many posts it saved.
- Posts take your app's fonts and colors. To change how they look, ask Lovable, for example: "Make the blog article text larger and add more space between paragraphs."
- If you make a new token in Kitful, ask Lovable to update the
KITFUL_READ_TOKENsecret in Lovable Cloud with it. - On a React with Vite app, the page sets its title, description and social preview tags in the browser, so a service that reads the HTML without running JavaScript may not see them. TanStack Start renders them on the server.
- The prompt adds no packages and no sample posts, and it leaves the rest of your app alone.
Troubleshoot
- Send test reports 401:
kitful-syncwas deployed with JWT verification on. Ask Lovable to deploy it withverify_jwt = false. - The sync reports a 401: the read token in Lovable Cloud's secrets is wrong or was replaced. Make a new token in Kitful and ask Lovable to update the
KITFUL_READ_TOKENsecret. - The blog page is empty: publish an article to the headless destination, then ask Lovable to run
kitful-synconce. - A post is missing: check that the Blog URL is your app's address followed by
/blog, then ask Lovable to runkitful-synconce. - Posts don't match the app's design: ask Lovable to style the article text with the site's typography: headings, lists, tables, quotes and code.
- Kitful refuses the Blog URL: it must be on your Kitful site's domain. See Before you start.
The full prompt
This is the prompt Copy prompt copies, so you can read it before you paste it. It holds no token: Lovable asks you for that.
Add a blog to this Lovable app that shows the posts I publish from Kitful. Kitful keeps the posts; this app copies them into its own database and renders them at /blog. Do only the steps below. Add no new packages, no sample or placeholder posts, and change no unrelated pages.
## Kitful API
- Base URL: https://kitful.ai/api/v1
- Every request sends `Authorization: Bearer <KITFUL_READ_TOKEN>`, from server code only. Never call Kitful from the browser and never put the token in client code.
- `GET /blog/posts?updated_since=<ISO time>&include=content&limit=25` returns `{ "data": [...], "next_cursor": string | null, "next_updated_since": string }`, changes oldest first. While `next_cursor` is not null, request the next page with the same query plus `&cursor=<next_cursor>`. `next_updated_since` is on the last page only.
- An item with `"status": "published"` is a post: `id`, `slug`, `url`, `locale`, `title`, `excerpt`, `tags`, `published_at`, `updated_at`, `featured_image` (`{ url, alt, width, height }` or null), `seo` (`{ title, description, canonical_url }`), `open_graph` (`{ title, description, image_url }`), `headings`, `content` (`{ html, markdown }`) and `json_ld`.
- Any other status, today `"deleted"`, means the post left the site; such an item has only `id`, `slug` and `updated_at`.
- Errors are JSON `{ "error", "message", "code" }`. On 401 (`invalid_token`) stop and tell me the token is wrong. On 400 for a cursor, start the run again without it.
## 1. Setup
Enable Lovable Cloud if it is not on. Check which stack this project uses: TanStack Start (server routes) or React with Vite (client routes), and follow the matching branch below.
## 2. Secret
Ask me for my Kitful read token through the secure secret input of Lovable Cloud and store it as `KITFUL_READ_TOKEN`. Do not ask me to paste it into the chat, and never print or log it.
## 3. Data
Create two tables:
- `kitful_posts`: id (text, primary key, Kitful's post id), slug (unique), url, locale, title, excerpt, tags (text array), published_at, updated_at, seo_title, seo_description, canonical_url, og_title, og_description, og_image_url, featured_image (jsonb), headings (jsonb), content_html (text), json_ld (jsonb).
- `kitful_sync_state`: one row, inserted when you create the table, with updated_since (text, `1970-01-01T00:00:00Z`), lease_until (timestamptz, null when no sync runs), pending (boolean, false) and last_run_at (timestamptz).
Turn on row-level security for both. `kitful_posts` gets one policy, SELECT for everyone; `kitful_sync_state` gets none. Only the sync writes either table, with the service role key inside its edge function.
## 4. Sync
An edge function named `kitful-sync`. Deploy it with JWT verification off (`verify_jwt = false` for this function in `supabase/config.toml`), because Kitful calls it without a login. On POST it starts the sync with `EdgeRuntime.waitUntil(...)` and answers `200` at once.
The sync:
1. Sets pending to true, then takes the lease in one atomic update: lease_until to now plus 10 minutes and last_run_at to now, only where lease_until is null or in the past. If no row was updated, another sync is running and will read again: stop.
2. Sets pending to false, then reads every page from the stored updated_since, as described above, with a 30-second timeout on each Kitful request.
3. For each post, first deletes any stored post with the same slug and a different id, then saves the post by its id, updating every field including the slug. For each item that is not published, deletes the stored post with that id.
4. After the last page succeeds, stores its next_updated_since as updated_since. If any page fails, keeps the old updated_since so the next run repeats the work.
5. Sets lease_until to null, also when the run fails. Then, if pending is true, starts again from step 1, at most three times in a row.
The same post can arrive in two runs; saving by id makes that harmless. The sync never needs the notification's body.
## 5. Schedule
A Lovable Cloud job that calls `kitful-sync` every hour with pg_cron and pg_net, named `kitful-sync-hourly`. Replace a job with that name instead of adding a second one.
Then run the sync once now, and tell me how many posts it saved.
## 6. Pages
TanStack Start: render `/blog` and `/blog/$slug` on the server, reading `kitful_posts` in a loader. React with Vite: read `kitful_posts` with the Supabase client in the page.
- `/blog`: every post, newest `published_at` first, each with its title linking to `/blog/<slug>`, its excerpt and its date. Use the site's existing layout, fonts and colors.
- `/blog/<slug>`: the title as the page's only h1, the featured image with its alt text, width and height when there is one, then content_html rendered as HTML. A slug with no stored post shows the app's normal not-found page.
- Style the article HTML with the site's typography: headings, paragraphs, lists, tables, blockquotes, code blocks, and images at most 100% wide.
- Add a Blog link to the site's navigation.
## 7. SEO for each post page
Set these the way the app already sets page titles and meta tags; if it has no way, set them from the page itself, without a new package.
- Title from seo_title, meta description from seo_description, and a canonical link to canonical_url.
- og:title, og:description, og:image and og:type article from the open_graph fields, and twitter:card summary_large_image when there is an image.
- json_ld printed in a `<script type="application/ld+json">` tag, with every `<` written as `\u003c`.
- A sitemap listing each post's `url`, with its updated_at as lastmod. Leave out a post whose canonical_url points to another page. TanStack Start: a server route at `/blog/sitemap.xml`. React with Vite: an edge function `kitful-sitemap` (JWT verification off) that returns the same XML, because a static file would miss new posts. Add its absolute URL as a `Sitemap:` line in `robots.txt`, and keep every rule already there.
## 8. Finish
End with the notification URL I paste into Kitful: the full public URL of `kitful-sync`. Remind me to publish the app first, and that Kitful's Blog URL must be this app's published address followed by `/blog`, on the same domain as my Kitful site.