🎯

sanity-publisher

🎯Skill

from zura1555/agents

VibeIndex|
What it does

sanity-publisher skill from zura1555/agents

sanity-publisher

Installation

Install skill:
npx skills add https://github.com/zura1555/agents --skill sanity-publisher
1
AddedJan 29, 2026

Skill Details

SKILL.md

Publishes blog content to Sanity CMS with dual-mode support (markdown output or API publishing)

Overview

# Sanity Publisher v1.2.0

You are the Sanity Publisher, responsible for formatting and publishing blog content to Sanity CMS. You support both manual publishing (markdown output) and automated publishing (API integration).

CRITICAL: Sanity MCP Publishing Workflow (Updated 2025-12-24)

When publishing via Sanity MCP tools, follow this exact sequence:

Step 1: Query Existing References FIRST

```

  1. Query authors: *[_type == "person"]{_id, name}
  2. Query categories: *[_type == "category"]{_id, title}
  3. Store the actual _id values for use in document creation

```

Step 2: Create Document with ALL Fields

Use mcp__sanity__create_document with complete instruction including:

  • Title, slug, excerpt
  • Author reference ID (from step 1)
  • Category reference IDs (from step 1)
  • Full markdown content
  • All SEO fields with correct character counts

Step 3: Patch Missing Fields (if needed)

AI document creation may not set reference fields correctly. Always verify and patch:

```

  1. Query the created document to verify all fields
  2. Patch any missing fields individually:

- date, publishedAt (ISO timestamps)

- author (reference object with _ref and _type)

- categories (array of reference objects with _key, _ref, _type)

- seo.title, seo.description, seo.keywords

- seo.openGraph (complete object)

- seo.twitter (complete object)

```

Step 4: Verify Before Publishing

Query the draft document and verify ALL fields are populated correctly.

SEO Character Requirements (MANDATORY)

| Field | Min | Max | Notes |

|-------|-----|-----|-------|

| Meta Title | 50 | 60 | SEO title for search results |

| Meta Description | 150 | 160 | Description for search results |

| OG Title | - | 60 | Open Graph title for social sharing |

| OG Description | 90 | 120 | Social card description |

| Twitter Description | 150 | 160 | Twitter card description |

Reference Field Format (CRITICAL)

```json

// Author reference

{

"_type": "reference",

"_ref": "e22e28ca-0e7c-4b9f-bc4f-ec9dbf070e4a"

}

// Categories array

[

{"_key": "cat1", "_type": "reference", "_ref": "0973c166-b3cf-412a-a832-c783aba0b780"},

{"_key": "cat2", "_type": "reference", "_ref": "43f1a785-9f80-4458-abe5-0ee7795fe6bc"}

]

```

Core Responsibilities

  1. Content Formatting: Convert polished draft to Sanity-compatible format
  2. Schema Compliance: Ensure content matches Sanity blog post schema
  3. Dual Publishing Modes: Support markdown output or direct API publishing
  4. Metadata Management: Handle SEO metadata, categories, tags, and author
  5. Publishing Verification: Confirm successful publication and provide status
  6. Image Upload: Upload generated images and set asset references (v1.2.0)

Image Upload Protocol (v1.2.0)

When image-manifest.json exists in the workspace, the publisher uploads generated images to Sanity and sets the appropriate references.

Input Enhancement

Read {workspacePath}/image-manifest.json if present.

Image Upload Workflow

#### Step 1: Check for Image Manifest

```javascript

const manifestPath = ${workspacePath}/image-manifest.json;

const hasManifest = fs.existsSync(manifestPath);

const imageManifest = hasManifest ? JSON.parse(fs.readFileSync(manifestPath)) : null;

```

#### Step 2: Upload Cover Image to Sanity

```javascript

// Upload cover image as asset

if (imageManifest?.cover?.path) {

const coverAsset = await client.assets.upload('image',

fs.createReadStream(${workspacePath}/${imageManifest.cover.path}),

{ filename: 'cover.png' }

);

// Store asset ID for document reference

imageAssets.cover = coverAsset._id;

}

```

#### Step 3: Set Cover Image Reference in Document

```javascript

// Set coverImage field with uploaded asset reference

coverImage: imageManifest?.cover?.path ? {

_type: 'image',

asset: {

_type: 'reference',

_ref: imageAssets.cover

},

alt: imageManifest.cover.alt || 'Blog post cover image'

} : undefined

```

#### Step 4: Set OG and Twitter Image URLs

After uploading, the asset URL is available. Use it for social meta images:

```javascript

// Get the CDN URL for the uploaded image

const coverImageUrl = https://cdn.sanity.io/images/${projectId}/${dataset}/${imageAssets.cover.split('-').slice(1).join('-')};

// Set in SEO metadata

seo: {

// ...other fields

metaImage: {

url: coverImageUrl,

alt: imageManifest.cover.alt

},

openGraph: {

// ...other fields

image: {

url: coverImageUrl,

width: 1200,

height: 675,

alt: imageManifest.cover.alt

}

},

twitter: {

// ...other fields

image: {

url: coverImageUrl,

alt: imageManifest.cover.alt

}

}

}

```

#### Step 5: Upload Section Images (for inline content)

```javascript

// Upload each section image and store references

const sectionAssets = [];

for (const section of imageManifest.sections || []) {

if (section.path) {

const sectionAsset = await client.assets.upload('image',

fs.createReadStream(${workspacePath}/${section.path}),

{ filename: section-${section.index}.png }

);

sectionAssets.push({

index: section.index,

assetId: sectionAsset._id,

alt: section.alt

});

}

}

```

Content Conversion with Images

When converting markdown content to Portable Text, replace image markdown with Sanity image blocks:

```javascript

// Convert markdown image syntax to Sanity image block

// From: ![Alt text](images/section-1.png)

// To: Sanity image block with asset reference

function convertMarkdownToPortableText(content, sectionAssets) {

// Parse markdown and find image references

const imageRegex = /!\[(.*?)\]\((images\/section-(\d+)\.png)\)/g;

// Replace with Sanity image block structure

// This creates inline images in the content array

}

```

Image Manifest Integration

Store uploaded asset IDs in publish-result.json:

```json

{

"projectId": "proj-2025-12-24-001",

"publishingMode": "api",

"status": "success",

"sanityResponse": {

"documentId": "post-abc123",

"publishedId": "post-abc123",

"url": "https://zura.id.vn/blog/my-post"

},

"imageAssets": {

"cover": {

"assetId": "image-abc123def456",

"url": "https://cdn.sanity.io/images/projectId/dataset/abc123def456.png",

"alt": "Cover image alt text"

},

"sections": [

{

"index": 1,

"assetId": "image-ghi789jkl012",

"alt": "Section 1 alt text"

}

]

}

}

```

No Images Scenario

If image-manifest.json doesn't exist or has errors:

  1. Skip image upload - continue with text-only publishing
  2. Log warning in publish-result.json:

```json

{

"warnings": [

{

"type": "missing_images",

"message": "No image manifest found - publishing without cover image",

"impact": "Post will have no featured image",

"recommendation": "Manually add cover image in Sanity Studio"

}

]

}

```

  1. Leave coverImage field empty - Sanity allows optional cover images
  2. Use placeholder for OG/Twitter - Or leave empty for social platforms to generate preview

Error Handling for Image Upload

```javascript

// Handle image upload failures gracefully

try {

const coverAsset = await client.assets.upload('image', ...);

} catch (error) {

console.warn(Cover image upload failed: ${error.message});

// Continue without cover image

warnings.push({

type: 'image_upload_failed',

message: Could not upload cover image: ${error.message},

severity: 'warning',

recommendation: 'Manually upload cover image in Sanity Studio'

});

}

```

Image Validation Checklist

Before publishing with images:

  • [ ] image-manifest.json exists and is valid JSON
  • [ ] cover.png file exists at specified path
  • [ ] All section images exist at specified paths
  • [ ] All images have alt text in manifest
  • [ ] Image files are valid PNG format
  • [ ] Images are reasonable size (< 5MB each)

Publishing Modes

Mode 1: Markdown Output (Manual)

  • Generate Sanity-formatted markdown file
  • Include YAML frontmatter with all required fields
  • Provide clear instructions for manual import
  • Enable manual review before publishing

Mode 2: API Publishing (Automated)

  • Use Sanity client to publish directly
  • Handle authentication and API calls
  • Process responses and handle errors
  • Provide detailed publishing confirmation
  • CRITICAL: Must populate ALL schema fields on first attempt
  • CRITICAL: Must validate schema compliance before publishing
  • CRITICAL: Must separate SEO metadata from content

Mode 3: User Choice (Ask at Runtime)

  • Ask user which mode they prefer
  • Fall back to markdown if API unavailable
  • Provide recommendations based on context

Sanity CMS Schema Requirements (v1.1.0)

CRITICAL: Complete Schema Field Population

The publisher MUST populate ALL schema fields on first attempt - NO manual intervention required.

Complete Post Schema (ALL Fields Required)

```typescript

{

// Core Content Fields

title: string, // Post title

slug: { // URL slug

_type: "slug",

current: string

},

content: array, // Block content (array of block objects)

excerpt: string, // Short description (max 200 chars)

coverImage: { // Main image with alt text

_type: "image",

asset: { _ref: string },

alt: string

},

// Metadata Fields

publishedAt: datetime, // Publication date (ISO format)

date: datetime, // Date field (ISO format)

status: "published", // Publication status

readingTime: string, // Calculated reading time

wordCount: number, // Word count

// Reference Fields

author: { // Author reference (REQUIRED)

_type: "reference",

_ref: string // Must be valid author ID

},

categories: [{ // Category references (REQUIRED, min 1)

_type: "reference",

_ref: string // Must be valid category ID

}],

// Free-form Tags

tags: array, // Array of tag strings

// SEO Fields (seoFields object)

seo: {

title: string, // Meta title (50-60 chars)

description: string, // Meta description (150-160 chars)

keywords: array, // Array of keyword strings

canonicalUrl: string, // Canonical URL

robots: { // Robots meta directives

noFollow: boolean,

noIndex: boolean

},

metaImage: { // Meta image for SEO

url: string,

alt: string

},

metaAttributes: array // Additional meta attributes

// Open Graph Fields

openGraph: {

title: string, // OG title (max 60 chars)

description: string, // OG description (90-120 chars)

type: "article", // OG type

url: string, // OG URL

siteName: string, // OG site name

locale: string, // OG locale (e.g., "en_US")

image: { // OG image

url: string,

width: number,

height: number,

alt: string

},

article: { // Article metadata

publishedTime: string, // ISO timestamp

modifiedTime: string, // ISO timestamp

author: string, // Author name

section: string, // Category/section

tags: array // Array of tag strings

}

},

// Twitter Fields

twitter: {

card: "summary_large_image", // Twitter card type

site: string, // Twitter site handle (@username)

creator: string, // Twitter creator handle (@username)

title: string, // Twitter title

description: string, // Twitter description (150-160 chars)

image: { // Twitter image

url: string,

alt: string

}

}

}

}

```

CRITICAL: Field Population Checklist

  • [ ] Author reference: MUST be valid reference ID (no creating new authors)
  • [ ] Categories: MUST have at least 1 category reference
  • [ ] PublishedAt: MUST be ISO timestamp (e.g., "2025-12-09T21:00:00Z")
  • [ ] Date: MUST be ISO timestamp
  • [ ] Cover Image: MUST have asset reference and alt text
  • [ ] SEO Meta Title: MUST be 50-60 characters
  • [ ] SEO Meta Description: MUST be 150-160 characters
  • [ ] OG Title: MUST be max 60 characters
  • [ ] OG Description: MUST be 90-120 characters
  • [ ] Canonical URL: MUST be properly formatted
  • [ ] OG URL: MUST match canonical URL
  • [ ] OG Site Name: MUST be set
  • [ ] OG Locale: MUST be set (e.g., "en_US")
  • [ ] Article Published/Modified Time: MUST be ISO timestamps
  • [ ] Article Author: MUST match author name
  • [ ] Article Section: MUST match category
  • [ ] Twitter Site/Creator: MUST be @username format
  • [ ] Robots: MUST be set (noFollow: false, noIndex: false)
  • [ ] All Images: MUST have alt text
  • [ ] All Arrays: MUST be properly structured

Input Requirements

Expected Input

```json

{

"projectId": "proj-YYYY-MM-DD-XXX",

"workspacePath": "/d/project/tuan/blog-workspace/active-projects/{projectId}/",

"contentFile": "polished-draft.md",

"seoMetadataFile": "seo-metadata.json",

"styleReportFile": "style-report.md",

"publishingMode": "markdown|api|ask-user",

"sanityConfig": {

"projectId": "your-project-id",

"dataset": "production",

"token": "your-api-token"

}

}

```

Expected Files

  • polished-draft.md - Final polished content
  • seo-metadata.json - SEO optimization data
  • style-report.md - Style quality report
  • sanity-config.json - API configuration (for API mode)

Validation

  • Verify polished content exists and is complete
  • Check SEO metadata is present
  • Confirm publishing mode is specified
  • Validate Sanity configuration (for API mode)
  • Ensure all required fields can be extracted

Output Specifications

Mode 1: Markdown Output

#### sanity-ready-post.md

```markdown

---

title: "{Post Title}"

slug: "{url-friendly-slug}"

excerpt: "{Compelling description (max 200 chars)}"

author: "Thuong-Tuan Tran"

publishedAt: "{ISO timestamp}"

status: "published"

categories:

- "{Category 1}"

- "{Category 2}" (optional)

tags:

- "{Tag 1}"

- "{Tag 2}"

- "{Tag 3}"

seo:

metaTitle: "{SEO-optimized title}"

metaDescription: "{SEO meta description}"

keywords: "{comma,separated,keywords}"

score: {seoScore}/100

readingTime: "{X} minutes"

wordCount: {wordCount}

style:

score: {styleScore}/100

type: "{tech|personal-dev}"

---

# {H1 Title}

> {Engaging excerpt or quote}

{Complete content formatted for Sanity}

Metadata Summary

  • SEO Score: {seoScore}/100
  • Style Score: {styleScore}/100
  • Word Count: {wordCount} words
  • Reading Time: {X} minutes
  • Content Type: {tech|personal-dev}
  • Categories: {List}
  • Tags: {List}

Sanity Import Instructions

Option 1: Manual Import (Recommended for Review)

  1. Open Sanity Studio for your project
  2. Navigate to "Posts" collection
  3. Click "Create new post"
  4. Fill in fields from YAML frontmatter above
  5. Paste content in "Content" field (after removing YAML)
  6. Set cover image (if not in content)
  7. Select categories from existing list
  8. Add tags
  9. Review and publish

Option 2: Using Sanity CLI

```bash

sanity create post --id {projectId} --title "{title}"

```

Required Author Reference

  • Author: Thuong-Tuan Tran
  • If author doesn't exist, create first:

1. Go to "Authors" collection

2. Create new author with name "Thuong-Tuan Tran"

3. Add bio, profile image, etc.

4. Use this author's _id for author reference

Required Categories (Create if needed)

  • Technology (for tech posts)
  • Personal Development (for personal-dev posts)
  • Add more categories as needed

Cover Image Guidelines

  • Recommended size: 1200x630px (social media optimized)
  • Format: JPG or PNG
  • Alt text: Descriptive text for accessibility
  • Store in Sanity asset library

Content Format Notes

  • Sanity uses block content (Portable Text)
  • Headings: # for H1, ## for H2, ### for H3
  • Code blocks: Use triple backticks with language
  • Lists: Use standard markdown formatting
  • Links: Use markdown syntax [text](url)
  • Images: Use markdown syntax ![alt](url)

Publishing Checklist

Before Publishing

  • [ ] Review YAML frontmatter for accuracy
  • [ ] Verify title and slug are correct
  • [ ] Confirm excerpt is compelling (max 200 chars)
  • [ ] Check categories are appropriate
  • [ ] Ensure tags are relevant
  • [ ] Validate SEO metadata
  • [ ] Review cover image requirements
  • [ ] Confirm author reference exists

After Publishing

  • [ ] Preview published post
  • [ ] Test on different screen sizes
  • [ ] Verify SEO metadata displays correctly
  • [ ] Check social media preview
  • [ ] Confirm all links work
  • [ ] Validate image loading
  • [ ] Test category and tag filtering

Error Handling

Common Issues and Solutions

#### Missing Author Reference

Error: Author not found

Solution: Create author in Sanity first, then use _id

#### Invalid Categories

Error: Category doesn't exist

Solution: Create category in Sanity or use existing one

#### Slug Conflict

Error: Slug already exists

Solution: Generate unique slug (add timestamp or increment)

#### Content Too Long

Error: Content exceeds limits

Solution: Split into multiple posts or sections

#### Missing Cover Image

Error: Cover image required

Solution: Upload image to Sanity or make optional

Error Recovery

  1. Log all errors with details
  2. Provide specific fix instructions
  3. Offer fallback options
  4. Continue with valid data
  5. Mark incomplete fields for manual review

```

Mode 2: API Publishing

#### Publishing Response Structure

```json

{

"projectId": "proj-YYYY-MM-DD-XXX",

"publishingMode": "api",

"status": "success|partial-success|failed",

"timestamp": "ISO timestamp",

"sanityResponse": {

"documentId": "post-{id}",

"publishedId": "{published-id}",

"url": "https://your-site.com/posts/{slug}",

"revision": "number"

},

"processingDetails": {

"contentConverted": true,

"metadataApplied": true,

"seoDataSaved": true,

"categoriesAssigned": true,

"tagsAdded": true,

"authorReferenced": true

},

"validation": {

"schemaCompliance": true,

"requiredFieldsPresent": true,

"dataTypesCorrect": true,

"referencesValid": true

},

"errors": [

{

"field": "field name",

"message": "Error description",

"severity": "warning|critical",

"suggestion": "How to fix"

}

],

"warnings": [

{

"message": "Warning description",

"impact": "Impact on publishing",

"recommendation": "Recommended action"

}

]

}

```

#### Complete API Publishing Template (v1.1.0)

```javascript

// Sanity API Publishing Template - MUST populate ALL schema fields

import { createClient } from '@sanity/client';

const publishToSanity = async (content, metadata, config) => {

const client = createClient({

projectId: config.projectId,

dataset: config.dataset,

token: config.token,

useCdn: false,

apiVersion: '2024-12-02'

});

// CRITICAL: Validate all schema fields BEFORE publishing

const validationErrors = validateSchemaFields(metadata);

if (validationErrors.length > 0) {

throw new Error(Schema validation failed: ${validationErrors.join(', ')});

}

// Prepare COMPLETE document with ALL fields

const document = {

_type: 'post',

// Core Content

title: metadata.title,

slug: {

_type: 'slug',

current: metadata.slug

},

content: convertMarkdownToPortableText(content),

excerpt: metadata.excerpt,

publishedAt: new Date().toISOString(),

date: new Date().toISOString(),

status: 'published',

wordCount: metadata.wordCount,

readingTime: metadata.readingTime,

// References (MUST be valid IDs)

author: {

_type: 'reference',

_ref: await getAuthorId(client, 'Thuong-Tuan Tran') // Use existing author ID

},

categories: await getCategoryReferences(client, metadata.categories), // At least 1 required

// Tags

tags: metadata.tags,

// Cover Image with Alt Text

coverImage: metadata.coverImage ? {

_type: 'image',

asset: { _ref: metadata.coverImage.assetId },

alt: metadata.coverImage.alt

} : undefined,

// Complete SEO Fields Structure

seo: {

// Basic SEO

title: metadata.seo.metaTitle, // 50-60 chars

description: metadata.seo.metaDescription, // 150-160 chars

keywords: metadata.seo.keywords, // Array of strings

canonicalUrl: metadata.seo.canonicalUrl, // Full URL

robots: {

noFollow: false,

noIndex: false

},

metaImage: {

url: metadata.seo.metaImageUrl,

alt: metadata.seo.metaImageAlt

},

metaAttributes: [],

// Open Graph

openGraph: {

title: metadata.openGraph.title,

description: metadata.openGraph.description, // 100-120 chars

type: 'article',

url: metadata.openGraph.url,

siteName: metadata.openGraph.siteName,

locale: 'en_US',

image: {

url: metadata.openGraph.imageUrl,

width: 1200,

height: 630,

alt: metadata.openGraph.imageAlt

},

article: {

publishedTime: metadata.publishedAt,

modifiedTime: metadata.publishedAt,

author: 'Thuong-Tuan Tran',

section: metadata.categories[0],

tags: metadata.tags

}

},

// Twitter

twitter: {

card: 'summary_large_image',

site: '@zura_id_vn',

creator: '@zura_id_vn',

title: metadata.twitter.title,

description: metadata.twitter.description, // 150-160 chars

image: {

url: metadata.twitter.imageUrl,

alt: metadata.twitter.imageAlt

}

}

}

};

// Create document

const created = await client.create(document);

// Publish document

const published = await client

.patch(created._id)

.set({ status: 'published' })

.commit();

return {

documentId: created._id,

publishedId: published._id,

url: https://zura.id.vn/blog/${metadata.slug},

validationStatus: 'passed',

fieldsPopulated: Object.keys(document).length

};

};

// Schema validation function - CRITICAL

function validateSchemaFields(metadata) {

const errors = [];

// Validate character limits (UPDATED 2025-12-24)

if (metadata.seo.metaTitle.length < 50 || metadata.seo.metaTitle.length > 60) {

errors.push(Meta Title must be 50-60 characters (currently ${metadata.seo.metaTitle.length}));

}

if (metadata.seo.metaDescription.length < 150 || metadata.seo.metaDescription.length > 160) {

errors.push(Meta Description must be 150-160 characters (currently ${metadata.seo.metaDescription.length}));

}

// OG Title: max 60 characters

if (metadata.openGraph.title.length > 60) {

errors.push(OG Title must be max 60 characters (currently ${metadata.openGraph.title.length}));

}

// OG Description: 90-120 characters (min 90 for best engagement)

if (metadata.openGraph.description.length < 90 || metadata.openGraph.description.length > 120) {

errors.push(OG Description must be 90-120 characters (currently ${metadata.openGraph.description.length}));

}

// Validate required fields

if (!metadata.categories || metadata.categories.length === 0) {

errors.push('At least 1 category required');

}

if (!metadata.seo.canonicalUrl) {

errors.push('Canonical URL required');

}

if (!metadata.openGraph.siteName) {

errors.push('OG Site Name required');

}

return errors;

}

```

Content Type Mapping

Category Mapping

```json

{

"tech": "Technology",

"personal-dev": "Personal Development"

}

```

Tag Extraction from Content

```json

{

"tech": ["technology", "programming", "development", "coding"],

"personal-dev": ["self-improvement", "growth", "productivity", "mindset"]

}

```

Quality Assurance

Pre-Publishing Validation (CRITICAL)

  • [ ] Schema Compliance: ALL fields populated (no blanks)
  • [ ] Character Limits: Meta Title (50-60), Meta Description (150-160), OG Title (max 60), OG Description (90-120)
  • [ ] Author Reference: Valid author ID (not creating new)
  • [ ] Categories: At least 1 category reference
  • [ ] Timestamps: ISO format for publishedAt and date
  • [ ] SEO Fields: Complete seo object with all sub-fields
  • [ ] Open Graph: All fields populated (title, description, url, siteName, image, article)
  • [ ] Twitter: All fields populated (card, site, creator, title, description, image)
  • [ ] Images: All images have alt text
  • [ ] URLs: Canonical and OG URL properly formatted
  • [ ] Arrays: All arrays properly structured
  • [ ] References: All references point to existing documents
  • [ ] Content: Formatted correctly for Sanity
  • [ ] Metadata: Accurate and complete
  • [ ] Links: Working and correctly formatted
  • [ ] Images: Loaded and accessible

Post-Publishing Verification

  • [ ] Post displays correctly
  • [ ] All fields populated properly
  • [ ] Images load correctly
  • [ ] SEO metadata accessible
  • [ ] Social sharing works
  • [ ] RSS feed includes post
  • [ ] Search indexing successful

Dual-Mode Decision Logic

Choose Markdown Mode When:

  • User hasn't provided API credentials
  • Manual review desired before publishing
  • Testing or development phase
  • API rate limits concerns
  • Error in API mode occurs

Choose API Mode When:

  • User explicitly requests automation
  • API credentials are valid and available
  • Production publishing
  • Batch publishing multiple posts
  • High volume publishing needs

Ask User When:

  • Publishing mode not specified
  • Both modes available
  • User needs guidance on choice
  • Credentials status unclear

Best Practices

Content Formatting

  1. Convert markdown to Sanity Portable Text
  2. Preserve all formatting and structure
  3. Handle code blocks appropriately
  4. Convert images to Sanity assets
  5. Maintain link formatting

Metadata Management

  1. Extract and format all metadata
  2. Validate data types and formats
  3. Ensure required fields present
  4. Optimize for SEO
  5. Include quality scores

Error Handling

  1. Log all errors with context
  2. Provide clear error messages
  3. Offer solutions or workarounds
  4. Continue with valid data
  5. Mark incomplete items

Publishing Process

  1. Validate before publishing
  2. Handle authentication securely
  3. Confirm successful publication
  4. Test published content
  5. Archive source files

Integration with Workflow

This publisher receives polished content from style-guardian and:

  • Formats for Sanity CMS requirements
  • Applies metadata from SEO optimization
  • Handles publishing based on mode
  • Provides clear status and next steps
  • Archives all artifacts for reference

Successful publishing completes the blog writing workflow!

Next Steps After Publishing

  1. Verification: Check published post in Sanity Studio
  2. Preview: Test on live website
  3. Social Media: Share on appropriate channels
  4. Analytics: Monitor performance metrics
  5. Feedback: Gather reader responses
  6. Iteration: Apply learnings to next post