How to use `sanity exec` to import content without browser globals?
The "window is not defined" error you're encountering happens because sanity exec runs your script in a Node.js environment, not a browser. When you try to import schema files or other code that references browser globals like window, it fails.
Here's how to write import scripts that work with sanity exec:
Basic Import Script Structure
Create a script file (e.g., scripts/import.ts) that uses the Sanity client in a Node environment:
// scripts/import.ts
import {getCliClient} from 'sanity/cli'
const client = getCliClient()
async function importContent() {
console.log(`Importing to:`)
console.log(`Project ID: ${client.config().projectId}`)
console.log(`Dataset: ${client.config().dataset}`)
// Your import logic here
const documents = [
{
_type: 'post',
_id: 'my-post-id',
title: 'My Post Title',
}
]
const transaction = client.transaction()
documents.forEach(doc => {
transaction.createOrReplace(doc)
})
await transaction.commit()
.then(() => console.log('Import complete!'))
.catch(err => console.error('Import failed:', err))
}
importContent()Run it with:
npx sanity exec scripts/import.ts --with-user-tokenThe --with-user-token flag provides authentication using your credentials.
Avoiding Schema Import Issues
If you need to reference your schema types but get window is not defined errors, you have a few options:
Option 1: Don't import the full schema
Instead of importing schema files directly, just use plain objects that match your schema structure. The client doesn't need the schema definitions to create documents.
Option 2: Import only type definitions
If you're using TypeScript, import just the generated types (not the schema):
import type {Post} from '../sanity.types'Option 3: Use Schema.compile for validation (as shown in the demo content guide)
If you need schema validation, compile it specifically for Node:
import {Schema} from '@sanity/schema'
import {schemaTypes} from '../schemas'
const defaultSchema = Schema.compile({types: schemaTypes})
const postSchema = defaultSchema.get('post')Complete Working Example
Here's a more complete example that imports content with transactions in batches to avoid rate limits:
// scripts/import.ts
import {getCliClient} from 'sanity/cli'
import type {SanityDocumentLike} from 'sanity'
const client = getCliClient()
// Your source data (could come from an API, file, etc.)
const sourceData = [
{title: 'Post 1', slug: 'post-1'},
{title: 'Post 2', slug: 'post-2'},
// ... more data
]
async function importContent() {
console.log(`Starting import...`)
// Transform your source data to Sanity documents
const documents: SanityDocumentLike[] = sourceData.map(item => ({
_type: 'post',
_id: item.slug, // Use source ID for idempotency
title: item.title,
}))
// Create in batches to avoid rate limits
const BATCH_SIZE = 100
for (let i = 0; i < documents.length; i += BATCH_SIZE) {
const batch = documents.slice(i, i + BATCH_SIZE)
const transaction = client.transaction()
batch.forEach(doc => transaction.createOrReplace(doc))
await transaction.commit()
console.log(`Imported batch ${i / BATCH_SIZE + 1}`)
}
console.log('Import complete!')
}
importContent()Key Points
- Use
getCliClient()fromsanity/cli- it's configured for Node.js - Run with
--with-user-tokenflag for authenticated operations - Use
createOrReplace()for idempotent imports (can run multiple times safely) - Batch operations into transactions to reduce API calls and avoid rate limits
- Don't import browser-dependent code (like full Studio schemas with custom components)
More Resources
The demo content creation guide I linked above has extensive examples including:
- Uploading images during imports
- Converting HTML to Portable Text
- Creating references between documents
- Handling asynchronous operations
For more complex migrations with validation and better tooling, consider using sanity migration instead, which provides built-in batching, dry-run capabilities, and schema validation. The migration tooling is often preferred for content migrations as it provides additional conveniences and safety features.
Show original thread9 replies
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.