Next.js blog integration

Connect Spron to your Next.js blog by adding one route that receives ready articles and saves them in your site. The integration sends complete article content and images, while your code controls storage and whether each post becomes public or stays a draft.

How Next.js blog publishing works

Spron sends each ready article to a public HTTPS route in your Next.js project. The @spron/receiver package verifies the request, passes the article to your code, and returns the result to Spron.

Your saveArticle function decides where posts live, such as a database, CMS, or Markdown file. Save each post by its article ID so later deliveries update the same post instead of creating another one.

When you press Republish in the article editor, your blog receives the latest saved version. For Markdown files, save the article as a file, then rebuild and deploy your blog. Spron does not write to your Git repository.

  • Press Publish in an article to send it.
  • Use Send pending to send ready articles waiting for delivery.
  • Return external_id so Spron can keep the destination record.
  • Return published_url when the post is public.

Before you connect your Next.js blog

You need access to your blog’s code, a server that runs Node.js, and a public HTTPS address that can receive articles. You must also decide how your route will save posts and images.

A Spron workspace owner or admin can create the connection. Other members see the instruction to ask a workspace owner or admin to connect the blog.

  • A Next.js project with the App Router
  • A public route such as https://example.com/api/spron
  • Storage for article records or files
  • Storage for downloaded article images
  • Server environment variables for SPRON_ACCESS_TOKEN and SPRON_WEBHOOK_SECRET

Connect Next.js to Spron

  1. Install the receiver

    Run the install command in your Next.js project. The receiver provides the route handler and image download helper used by the integration.

    npm install @spron/receiver
  2. Add the article route

    Create app/api/spron/route.ts and add the receiver code below. Keep the route on the Node.js runtime.

    Write saveArticle and saveImage for your own storage. Save each article by its ID so an update changes the existing post.

    • Download images before saving the final article.
    • Revalidate the blog index and the saved post path.
    • Return only external_id for a non-public post.
    • Return both external_id and published_url for a public post.
    import { spronReceiver, downloadImages } from "@spron/receiver";
    import { revalidatePath } from "next/cache";
    import { saveArticle, saveImage } from "@/lib/spron-store";
    
    export const runtime = "nodejs";
    
    export const POST = spronReceiver({
      secret: process.env.SPRON_WEBHOOK_SECRET!,
      token: process.env.SPRON_ACCESS_TOKEN!,
      onArticle: async (article, delivery) => {
        const media = await downloadImages(article, saveImage);
        const post = await saveArticle({ ...article, ...media }, delivery);
        revalidatePath("/blog");
        revalidatePath(post.path);
        return post.isPublic
          ? { external_id: post.id, published_url: post.url }
          : { external_id: post.id };
      },
    });
  3. Connect your blog

    In Spron, open the Next.js integration and press Connect blog. Enter the public address of your route, such as https://example.com/api/spron.

    Copy the token Spron shows and add it to your server as both SPRON_ACCESS_TOKEN and SPRON_WEBHOOK_SECRET. The signing secret is shown once when you create the connection, so store it securely.

    SPRON_ACCESS_TOKEN=your-token
    SPRON_WEBHOOK_SECRET=your-token
  4. Send your first article

    Deploy your blog so the public route uses the environment variables and storage code. Then press Publish in an article, or use Send pending from the connection.

    • Open Delivery history to see whether the article arrived.
    • Check the saved post in your storage or CMS.
    • If the post is public, open the returned URL and inspect the page.

What Spron sends to your blog

Spron sends the complete article data needed to save a post. This includes the title, formatted content, search description, images, alt text, and captions.

Use downloadImages before saving the article. Image downloads from Spron expire after one hour, so your route must copy them into storage controlled by your site.

The receiver checks the bearer token, request signature, timestamp, delivery ID, and request body before it calls onArticle. Its default delivery-age tolerance is 300 seconds, and its default streamed body limit is 2 MiB.

  • Use article.id as the stable key for an update.
  • Use the downloaded content_html when saving formatted content.
  • Return external_id with the saved post ID.
  • Add published_url only when the post has a public URL.

Check your first Next.js post

Open Delivery history in Spron after sending your first article. The table shows the article, connected site, result, and sent date.

A result can appear as Waiting, Sending, Saved as draft, Published, or Needs attention. If the route returns a public URL, open that URL and check the title, content, links, and locally saved images.

When a delivery needs attention, review the displayed delivery error. An owner or admin can retry accepted, published, or failed deliveries while the connection remains active.

  • Confirm that the article uses the expected destination path.
  • Check that article images load from your site’s storage.
  • Send an update with Republish and confirm that it changes the same post.

Fix common Next.js connection issues

The delivery shows Needs attention

Open the delivery error in Delivery history. Errors from saveArticle, saveImage, or another onArticle callback operation cause the route to fail so Spron can retry. Fix the storage or callback error, then use the retry action.

The route returns 401, 400, or 413

The receiver returns 401 for an invalid authorization or signature request, 400 for an invalid request, and 413 when the streamed body exceeds its configured limit. Check that both server variables contain the token shown by Spron and that no proxy changes the raw request body or signature headers.

Article images are missing after delivery

Make sure your route calls downloadImages and that saveImage stores each file on your site. Spron image downloads expire after one hour, and the downloader rejects redirects and times out after 10 seconds.

Two servers create duplicate posts

Upsert each post by article.id and use an atomic database claim or shared lock across processes. The receiver shares one write within a single receiver instance, but its in-memory replay check is not a distributed lock.

Need help connecting? Contact support, visit the Help Center, or see our other publishing integrations.

Next.js FAQ

We're here to help.

Do I need to write code?

Yes, a little. Add one Next.js route, then write <code>saveArticle</code> and <code>saveImage</code> so the route knows where to store posts and images.

Can I use a Next.js Markdown blog?

Yes. Save each article as a Markdown file, then rebuild and deploy the blog. Spron sends the article to your route but does not write to your Git repository.

How do I update an article?

Press <strong>Republish</strong> in the article editor. Spron sends the latest saved version, and your route should use <code>article.id</code> to update the same post.

Can the integration save drafts?

Yes. If your code saves a non-public post, return its <code>external_id</code> without a <code>published_url</code>. The delivery can appear as <strong>Saved as draft</strong>.

Can it work on more than one server?

Yes. Save posts by article ID and use a shared lock or atomic database upsert so two server processes do not write the same post at once.

How do I stop sending articles?

Use <strong>Turn off</strong> on the connection row. Posts already saved on your blog stay there.

What happens if I delete a published post?

When your route returned <code>published_url</code>, Spron can use <strong>Check destination</strong> to send a HEAD request to that URL. If it detects that the post was deleted, it can send the article again.

How does the receiver handle duplicate deliveries?

A duplicate delivery returns the earlier successful acknowledgement again, including <code>published_url</code> and <code>external_id</code> when available. Across multiple processes, use database-backed replay storage and an atomic write.