Frogpost
Guide

How to publish SEO blog posts to Contentful automatically

Contentful is a good home for a blog and a slow place to write one. This guide covers what it takes to publish SEO posts into Contentful without pasting them in by hand: the content model, how each field gets filled, what the rich text looks like, images, internal links, scheduling, and what your front end has to handle. Frogpost does all of it, from 3 free posts a month, and Pro is $10 a month.

Updated

Why publishing to Contentful by hand is slow

Contentful stores content as structured entries, not as pages. A blog post is an entry of a content type, and every part of the post lives in its own field: the title, the slug, the summary, the SEO fields, the image, and a rich text body. That structure is what makes a headless CMS worth using. It is also why copying a finished article from a document into Contentful takes so long.

Pasting into the rich text editor loses headings and turns lists into paragraphs. Every image has to be uploaded as an asset, processed, published, and then embedded. The slug and meta description are typed separately. The category has to match the exact values your content type allows. Multiply that by a post a week and the writing is no longer the bottleneck. Getting the post into the CMS is.

Automating it means doing the same work through the Contentful Management API: creating the entry, filling each field in the right format, uploading the assets, and publishing. The rest of this guide describes what that involves, in the order a post goes through it.

What you need before you start

  • A Contentful space and an environment. Most blogs publish to master.
  • A content type for blog posts, with at least a title, a slug and a rich text body.
  • Access to that space, either by signing in to Contentful through Frogpost, or with a personal access token.
  • A list of topics you want to rank for. Check that people actually search them before you queue them.
  • Your website address, so the posts can describe your product accurately and link to it.
  • Optional but worth it: the URL pattern your blog uses, like https://example.com/blog/{slug}, so posts can link to each other.

One Frogpost project is one blog: one Contentful connection, one content type, one queue, and one schedule. If you run two blogs, they are two projects, and neither can publish into the other.

The content model, and how each field gets filled

You do not have to rebuild your content type. Frogpost reads the fields on the type you picked and matches each one to a part of the post by its type and its field ID. This table shows the rules, so you can check your model before the first run.

How Frogpost matches Contentful fields
Part of the postContentful field typeMatched whenWhat gets written
TitleShort textID is title or ends in titleThe post headline
SlugShort textID contains slug or urlPathA lowercase, hyphenated URL slug
ExcerptShort or long textID contains excerpt, summary, or description (but not meta or seo)A 100 to 150 word summary
BodyRich textThe first rich text field on the typeThe full article as Contentful rich text
SEO titleShort textID contains metaTitle or seoTitleA title under 60 characters
Meta descriptionShort or long textID contains metaDesc or seoDescA 150 to 160 character description
Publish dateDate and timeThe first date field on the typeThe moment the entry is created
Featured imageMedia (one file)The first single media field on the typeAn uploaded, published image asset
Category or tagsShort text, or a list of short textFields you choose to control in settingsValues picked from your allowed list

When a field is not recognised

A field that does not match any rule is left empty. That is fine for optional fields. It is a problem for a required one, because Contentful will refuse to publish an entry with a required field missing. The quickest fix is usually the field ID, not the content model. A meta description field with the ID googleSnippet will not be found. Renaming the ID to seoDescription fixes it, and the name editors see can stay whatever you like.

A content type that works well

If you are creating a blog post type from scratch, this set of field IDs is matched without any changes: title, slug, excerpt, body (rich text), seoTitle, seoDescription, publishDate (date), featuredImage (media), and category. Make the slug field unique in its validations, so two posts can never share a URL.

What the rich text body looks like

The body is not pasted HTML or a markdown string. It is a Contentful rich text document, the same structure the editor produces, so it renders through your existing rich text renderer and stays editable in the Contentful web app.

Each post is written with a clear outline: four to six main sections, each with two or three subsections, and at least 1,500 words. That outline becomes these node types:

Rich text nodes used in generated posts
In the articleContentful rich text node
Section and subsection headingsheading-2 and heading-3
Paragraphsparagraph
Bold and italic textbold and italic marks
Links to your site and to earlier postshyperlink
Bulleted and numbered listsunordered-list and ordered-list
Images inside the articleembedded-asset-block

Tables, quotes and code blocks are not produced. If your renderer styles those, nothing breaks, they just will not appear in generated posts. A short excerpt of a real body looks like this:

Part of a rich text body field
{
  "nodeType": "document",
  "data": {},
  "content": [
    {
      "nodeType": "heading-2",
      "data": {},
      "content": [{ "nodeType": "text", "value": "Choosing a content model", "marks": [], "data": {} }]
    },
    {
      "nodeType": "paragraph",
      "data": {},
      "content": [
        { "nodeType": "text", "value": "Start with the fields your ", "marks": [], "data": {} },
        {
          "nodeType": "hyperlink",
          "data": { "uri": "https://example.com/blog/headless-cms-basics" },
          "content": [{ "nodeType": "text", "value": "headless CMS", "marks": [], "data": {} }]
        },
        { "nodeType": "text", "value": " already has.", "marks": [], "data": {} }
      ]
    },
    {
      "nodeType": "embedded-asset-block",
      "data": { "target": { "sys": { "type": "Link", "linkType": "Asset", "id": "4Xa9..." } } },
      "content": []
    }
  ]
}

Images and assets

Every post gets a featured image if your content type has a media field for it. Frogpost searches for a relevant photo, uploads it to your space as an asset, waits for Contentful to process it, publishes the asset, and links it to the entry. The asset title is the post title, so your media library stays searchable.

Inside the article, two to four images are added only where a picture helps the reader, such as when a named product or screen is being described. Each goes through the same upload and publish steps, and sits in the body as an embedded asset. If an image cannot be found or processed in time, the post is published without it. A missing picture never blocks a post.

Your front end has to render embedded assets. With the official React renderer, that is one entry in the options object:

Rendering embedded images with @contentful/rich-text-react-renderer
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { BLOCKS } from '@contentful/rich-text-types';

const options = {
  renderNode: {
    [BLOCKS.EMBEDDED_ASSET]: (node) => {
      const { file, title } = node.data.target.fields;
      const { width, height } = file.details.image;
      return <img src={`https:${file.url}?w=1200&fm=webp`} alt={title} width={width} height={height} loading="lazy" />;
    },
  },
};

export const PostBody = ({ body }) => documentToReactComponents(body, options);

The asset has to be resolved for node.data.target.fields to exist. The Contentful JavaScript client does that for you when the entry is fetched with its linked assets included, which is the default. If you use GraphQL, request body { json links { assets { block { sys { id } url title width height } } } } and look each asset up by ID.

Categories and tags that match your model

Free-text categories are how a blog ends up with Guides, Guide and How-to guides as three separate sections. To avoid that, you choose which fields Frogpost controls, such as a category, a section, or a list of tags, and give each one its allowed values. If the Contentful field already has a list of accepted values in its validations, those are picked up.

Each post must choose from that list, word for word. A single field gets one value. A list field gets one or more. If a required list comes back empty, the first allowed value is used so Contentful does not reject the entry. Fields you delete or rename in Contentful later are skipped instead of causing an error.

Draft, publish, or schedule

Publishing modes
ModeWhat happens in ContentfulGood for
DraftThe entry is created and left unpublished.Your first posts, while you check the tone and the fields.
Publish nowThe entry is created and published at once.A post you have already read and edited.
ScheduleAt each hour you set, in UTC, the oldest topics in the queue are written and published live.A steady cadence, once you trust the output.

You can edit a post in Frogpost after it is created, and the changes are written back to the same entry. You can also edit it in Contentful like any other entry. The publish date field is set when the entry is created, so it matches the order posts went out.

Scheduling is per project. Pick how many posts each run writes and which hours it runs at. If a topic fails, for example because a required field could not be filled, it is marked failed with the reason and the rest of the queue carries on.

What your front end has to handle

  • A route for each post, built from the slug. This is the same pattern you give Frogpost for internal links.
  • Rendering for heading-2, heading-3, lists, hyperlinks and embedded assets in the rich text body.
  • The SEO title and meta description in the page head. In Next.js that is generateMetadata reading the two fields.
  • A blog index and a sitemap that pick up new entries. A static site needs a webhook on Entry publish that revalidates or rebuilds the blog.

Contentful sends webhooks for publishes made through the API exactly as it does for publishes in the web app, so a scheduled post triggers your site the same way an editor's post does.

Frogpost and Contentful AI Actions

Contentful has its own AI features, called AI Actions. They are useful, and they solve a different problem.

Frogpost compared with Contentful AI Actions
Contentful AI ActionsFrogpost
Starts fromAn entry that already existsA topic in a queue
Typical jobsMeta descriptions, keyword suggestions, alt text, translation, rewriting a fieldWriting the whole post and creating the entry
ImagesAlt text for images you addedFinds, uploads and embeds the images
SchedulingRun by an editor inside an entryPublishes a queue at hours you set
PlanContentful Premium and EnterpriseFree for 3 posts a month, Pro is $10 a month, on any Contentful plan

They work together. A team can let Frogpost write and file the posts, and use AI Actions in Contentful to translate them.

A checklist for the first run

  1. Check the field IDs on your blog content type against the table above, and rename any required field that will not be matched.
  2. Connect the space in Frogpost and choose that content type.
  3. Add your website so Frogpost reads your product pages before it writes.
  4. Set the URL pattern for your posts so internal links work.
  5. Choose the category and tag fields you want controlled, and their allowed values.
  6. Queue three topics you know people search for, and save the first post as a draft.
  7. Open the draft in Contentful and on your site. Check the headings, the images, the SEO fields and the category.
  8. When the drafts look right, turn on a schedule.

Questions

How do I automatically publish SEO posts to Contentful?
Connect a Contentful space, pick the content type your blog posts use, and add topics to a queue. Frogpost writes each post, fills the title, slug, excerpt, rich text body, SEO title, meta description, publish date and featured image fields it finds on that content type, and creates the entry. A single post can be saved as a draft. A scheduled queue publishes entries live at the hours you choose, in UTC.
How is this different from Contentful AI Actions?
AI Actions works on an entry that already exists: it can draft a meta description, suggest keywords, write alt text, or translate a field. It is part of the Premium and Enterprise plans. Frogpost starts from a topic, writes the whole post, uploads its images as assets, and creates the entry in your content type.
Do I have to change my content model?
No. Frogpost reads the content type you already have and writes into the fields it can identify. Fields it cannot identify are left empty, so a required field it cannot fill will stop the entry from publishing. Renaming a field ID to something recognisable, like seoTitle, is usually enough.
Which Contentful fields does Frogpost fill?
Title, slug, excerpt, the rich text body, an SEO title, a meta description, a publish date, a featured image, and any category or tag fields you choose to control. The body is written as real Contentful rich text with headings, lists, links and embedded image assets.
Will the posts link to each other?
Yes, if you give the project a URL pattern such as https://example.com/blog/{slug}. Frogpost looks up earlier posts on the same blog that are closest in meaning to the new topic and links a few of them inline. Without a pattern it skips internal links, because a headless CMS does not know your site routes and the links would 404.
Does publishing trigger my site to rebuild?
Publishing through Frogpost is a normal Contentful publish, so any webhook you already have on Entry publish fires as usual. If your blog is statically generated, point that webhook at your revalidation or rebuild hook and new posts appear without a manual deploy.
What does it cost?
The free plan includes 3 posts a month, with no card required. Pro is $10 a month for unlimited posts and scheduled publishing, shared across every project on the account.

Connect Contentful and publish the first post free

Free includes 3 posts a month. Pro is $10 a month.