Post to Bluesky

Official(made by Sanity team)

By Ken Jones Pizza & Simon MacDonald

Inform your the Bluesky crowd when you publish a post

schemaTypes/post.ts

defineField({
  name: 'blueskyPost',
  title: 'Bluesky Post Content',
  type: 'text',
  description: 'Content to post on Bluesky when this post is published',
})

index.ts

import {env} from 'node:process'

import {BlueskyStrategy, Client} from '@humanwhocodes/crosspost'
import {documentEventHandler} from '@sanity/functions'

const {BLUESKY_USERNAME = '', BLUESKY_PASSWORD = '', BLUESKY_HOST = 'bsky.social'} = env

interface NotificationData {
  slug: {
    current: string
  }
  blueskyPost: string
  title: string
}

export const handler = documentEventHandler<NotificationData>(async ({event}) => {
  const {data} = event
  const {title, blueskyPost, slug} = data

  try {
    const bluesky = new BlueskyStrategy({
      identifier: BLUESKY_USERNAME,
      password: BLUESKY_PASSWORD,
      host: BLUESKY_HOST,
    })
    const client = new Client({
      strategies: [bluesky],
    })

    const postContent = `${title}

${blueskyPost}

${slug.current}`

    await client.post(postContent)
    console.log('Successfully sent post to Bluesky')
  } catch (error) {
    console.error('Error posting to Bluesky:', error)
    throw error
  }
})

sanity.blueprints.ts

import 'dotenv/config'
import process from 'node:process'
import {defineBlueprint, defineDocumentFunction} from '@sanity/blueprints'

const {BLUESKY_USERNAME, BLUESKY_PASSWORD, BLUESKY_HOST} = process.env
if (typeof BLUESKY_USERNAME !== 'string' || typeof BLUESKY_PASSWORD !== 'string') {
  throw new Error('BLUESKY_USERNAME and BLUESKY_PASSWORD must be set')
}

export default defineBlueprint({
  resources: [
    defineDocumentFunction({
      type: 'sanity.function.document',
      name: 'bluesky-post',
      src: './functions/bluesky-post',
      memory: 1,
      timeout: 10,
      event: {
        on: ['create'],
        filter: "_type == 'post' && defined(blueskyPost)",
        projection: '{title, blueskyPost, slug}',
      },
      env: {
        BLUESKY_USERNAME: BLUESKY_USERNAME,
        BLUESKY_PASSWORD: BLUESKY_PASSWORD,
        BLUESKY_HOST: BLUESKY_HOST || 'bsky.social',
      },
    }),
  ],
})

The Problem

Content teams want to automatically share their published articles on Bluesky to increase reach and engagement. Manually cross-posting to social platforms is time-consuming and often forgotten, leading to missed opportunities for content promotion.

The Solution

This Sanity Function automatically posts to Bluesky when content is published using the @humanwhocodes/crosspost library. When a post is created with the blueskyPost field populated, the function creates a Bluesky post containing the title, summary, and slug, helping maintain consistent social media presence without manual effort.

Quick Start

View full instructions and source code.

Initialize blueprints if you haven't already: npx sanity blueprints init

Then: npx sanity blueprints add function --example bluesky-post

Then deploy: npx sanity blueprints deploy

Bluesky Setup

More detailed instructions in the GitHub README

  1. Log into your Bluesky account: Go to bsky.app and sign in with your account
  2. Create an App Password: Navigate to Settings → Privacy and Security → App Passwords, click "Add App Password", give it a descriptive name, and click "Create App Password"
  3. Save your credentials: Add BLUESKY_USERNAME, BLUESKY_PASSWORD, and BLUESKY_HOST to your .env file

How It Works

When a post is published, the function automatically:

• Creates a Bluesky post containing the title, summary, and slug

• Uses the @humanwhocodes/crosspost library to handle posting

• Maintains a consistent social media presence without manual effort

• Ensures the post is shared immediately upon publication

Key Benefits

Automates social media sharing by posting to Bluesky on content publish

Saves time by eliminating manual cross-posting tasks

Ensures consistency with automatic posting of title, summary, and link

Increases content reach by maintaining active social media presence

Reduces missed opportunities by never forgetting to share published content

Technical Implementation

The function uses the @humanwhocodes/crosspost library to automate posting to Bluesky. It's built with:

• Event-driven architecture (triggers on publish)

• Integration with Bluesky API using app passwords

• Environment variable configuration for credentials

• Error handling for authentication and posting issues

Perfect For

• Content teams looking to automate social media posting

• Workflows that benefit from increased content reach and engagement

• Teams managing multiple social media accounts

• Projects that require consistent and timely content promotion

Contributors

Official Recipes by Sanity

First Published Timestamp Function

Featured contribution
Official(made by Sanity team)

Automatically track when content was first published with a timestamp that sets once and never overwrites, providing reliable publication history for analytics and editorial workflows.

Knut Melvær
Go to First Published Timestamp Function

Automatically tag blog posts

Featured contribution
Official(made by Sanity team)

AI-powered automatic tagging for Sanity blog posts that analyzes content to generate 3 relevant tags, maintaining consistency by reusing existing tags from your content library.

Go to Automatically tag blog posts