All guides

Writing and Publishing Content

Create entries, write rich content, manage drafts, and publish — from the dashboard or the API.

3 min read504 words

Once your schema is defined, you can start creating content. Airdraft has a built-in editor UI and a full REST API — use whichever fits your workflow.

Using the dashboard editor

Opening a collection

  1. In the sidebar, click the collection you want to write in (e.g. Posts).
  2. You see a list of existing entries with their status badges.
  3. Click New entry to create a new one.

The entry editor

The editor renders a form based on your collection's schema:

Field type Editor widget
string Single-line text input
text Multi-line textarea
rich-text Full Tiptap editor (headings, bold/italic, links, lists, code blocks, images)
number Number input
boolean Toggle switch
date / datetime Date/time picker
select Dropdown of defined options
media Media picker — opens the media library
relation Search and select an entry from the referenced collection
tags Tag input with comma-separated or Enter-separated tags

Autosave

The editor autosaves a local draft every 30 seconds. Autosaved content is stored in your browser and is visible only to you — it's not committed to storage until you explicitly save.

Saving a draft

Click Save draft to persist your changes to storage with status: "draft". Drafts are not returned by default when your frontend queries published content.

Publishing

When the entry is ready to go live:

  1. Click Publish to set status: "published" and publishedAt to the current time.
  2. The entry is immediately visible to API consumers querying published content.

To schedule a future publish, click the dropdown arrow next to Publish, set a date and time, and click Schedule. The entry will automatically transition to published at that time.

Editing an existing entry

Open the entry from the collection list. The editor loads the current published version. Any changes you make create a new revision — the previous sha is used for conflict detection.

Tip: If two people edit the same entry at the same time, the second save will fail with a conflict error. The editor shows the conflict and lets you choose which version to keep.

Deleting an entry

Open the entry and click the ⋯ menu → Delete. You must confirm deletion. Deleted entries are permanently removed (no soft delete).


Using the API

You can also manage content programmatically via the REST API or the TypeScript client.

Creating an entry

Using the server client (createCmsClient from @airdraft/next):

import { cms } from '@/lib/cms'  // createCmsClient(airdraft)

const entry = await cms.createEntry('posts', {
  title: 'My First Post',
  excerpt: 'An introduction.',
  body: '<p>Hello world!</p>',
  tags: ['intro', 'hello'],
})
// entry.slug → 'my-first-post'

Using the HTTP client (AirdraftClient from @airdraft/client):

import { client } from '@/lib/client'  // new AirdraftClient(...)

const result = await client.entries.create('posts', { title: 'My First Post' })

Updating an entry

Updates require the current sha (optimistic concurrency). With the server client:

const entry = await cms.getEntry('posts', 'my-first-post')
// entry.sha → content fingerprint

await cms.updateEntry('posts', 'my-first-post', {
  ...entry.data,
  title: 'Updated Title',
}, entry.sha)

With the HTTP client:

const entry = await client.entries.get('posts', 'my-first-post')
// entry.meta.sha → sha is in meta, not top-level

await client.entries.update('posts', 'my-first-post', {
  ...entry.data,
  title: 'Updated Title',
}, entry.meta.sha)

If the sha doesn't match (someone else edited the entry), the update throws a 409 Conflict error.

Publishing via the API

await cms.updateEntry('posts', 'my-first-post', {
  ...entry.data,
  status: 'published',
  publishedAt: new Date().toISOString(),
}, entry.sha)

Deleting an entry

// Server client
await cms.deleteEntry('posts', 'my-first-post', entry.sha)

// HTTP client
await client.entries.delete('posts', 'my-first-post', entry.meta.sha)

Entry status reference

Status Description
draft Saved but not visible to public queries
published Live — returned by default when querying without status filter
scheduled Will automatically transition to published at publishedAt
archived Manually hidden from public queries; preserves content

Resources

What's next