Developing with Next.js on GitHub Codespaces
A guide covering step-by-step instructions for developing Next.js applications with Sanity Studio Live Preview on GitHub Codespaces.
This developer guide was contributed by Eric Streske (Senior Solution Architect @ Sanity).
Overview
GitHub Codespaces provides a cloud-based development environment, but it presents unique challenges when working with:
- Dynamic URLs that change per codespace instance.
- Proxy headers that can cause CORS and Server Actions issues.
- WebSocket connections that don't work reliably through the Codespaces proxy.
This guide documents solutions to these challenges for a Next.js + Sanity Studio monorepo.
Prerequisites
- GitHub repository with Next.js and Sanity Studio.
- The examples in this guide follow the patterns from the Turbo Start Sanity template.
- Sanity project with project ID, dataset, and API tokens.
Setup
Configure Sanity CORS origins
Sanity's API requires explicit CORS configuration to allow requests from your Codespaces URLs.
Add CORS origins to Sanity
- Go to https://www.sanity.io/manage.
- Select your project.
- Navigate to API > CORS Origins.
- Add the following origins, replacing
YOUR_CODESPACE_NAMEwith the name of your codespace (the value of$CODESPACE_NAMEin the codespace terminal):https://YOUR_CODESPACE_NAME-3333.app.github.dev(Studio)https://YOUR_CODESPACE_NAME-3000.app.github.dev(frontend)
- For each origin:
- Click Add CORS origin, enter the origin, and select Allow credentials.
- Click Save.
Why exact origins instead of wildcards
- Each codespace has a unique name in its URL, so you add the origins for your own codespace.
- A wildcard such as
https://*.app.github.devmatches every codespace on GitHub, including ones you don't control. With credentials allowed, any of them could make authenticated requests as a logged-in user of your project. - Trade-off: When you create a new codespace, add its origins, and remove the old ones.
Security notes
- Never allow credentials on a platform-wide wildcard such as
https://*.app.github.dev. For the full guidance, see "Access your data (CORS)". - Remove a codespace's origins when you no longer use the codespace.
Configure port forwarding
Make ports public
GitHub Codespaces ports are private by default, which prevents external services (like Sanity) from accessing your development servers.
Method 1: VS Code UI
- Open the PORTS tab in the terminal panel.
- Right-click port 3000, and then select Port Visibility > Public.
- Right-click port 3333, and then select Port Visibility > Public.
Method 2: CLI
gh codespace ports visibility 3000:public -c $CODESPACE_NAME
gh codespace ports visibility 3333:public -c $CODESPACE_NAMEWhy this matters
- Sanity needs to make requests to your frontend for live preview.
- Studio needs to load the frontend in an iframe.
- Private ports block these cross-origin requests.
Set up environment variables
Front-end environment file
Create apps/web/.env and add the following environment variables:
# Sanity Configuration
NEXT_PUBLIC_SANITY_PROJECT_ID=<your-project-id>
NEXT_PUBLIC_SANITY_DATASET=production
NEXT_PUBLIC_SANITY_API_VERSION=2025-08-29
# API Tokens (get from https://sanity.io/manage)
SANITY_API_READ_TOKEN=your-token-here
SANITY_API_WRITE_TOKEN=your-token-here
# Studio URL (optional - auto-detected in Codespaces)
NEXT_PUBLIC_SANITY_STUDIO_URL=https://${CODESPACE_NAME}-3333.${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}Studio environment file
Create apps/studio/.env and add the following environment variables:
# Sanity Studio Configuration
SANITY_STUDIO_PROJECT_ID=<your-project-id>
SANITY_STUDIO_DATASET=production
SANITY_STUDIO_TITLE="Your Studio Title"
# Presentation URL (optional - auto-detected in Codespaces)
SANITY_STUDIO_PRESENTATION_URL=https://${CODESPACE_NAME}-3000.${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}Create API tokens
- Go to https://www.sanity.io/manage.
- Select your project.
- Navigate to API > Tokens.
- Create tokens:
- Read token: Viewer role
- Write token: Editor role
You can give these names like "Codespaces read" and "Codespaces write" to make them easier to recognize in the future.
Code changes for Codespaces compatibility
Detect Codespaces domain
In many cases (especially if using a starter template), the Presentation Tool URL defaults to http://localhost:3000 in development mode, which doesn't work in Codespaces.
Update any helper functions that determine the frontend URL to auto-detect Codespaces environment.
/**
* Determines the presentation URL based on the current environment.
* Priority order:
* 1. SANITY_STUDIO_PRESENTATION_URL environment variable (if set)
* 2. Codespaces: Dynamically constructed URL using CODESPACE_NAME
* 3. Local development: http://localhost:3000
* @throws {Error} If URL cannot be determined in production
*/
export const getPresentationUrl = () => {
// First priority: explicit environment variable
const presentationUrl = process.env.SANITY_STUDIO_PRESENTATION_URL;
if (presentationUrl) {
return presentationUrl;
}
// Second priority: GitHub Codespaces (browser-side detection)
// Codespaces URLs follow pattern: https://{codespace-name}-{port}.{domain}
if (typeof window !== "undefined") {
const hostname = window.location.hostname;
const codespacesMatch = hostname.match(/^(.+?)-(\d+)\.(.+)$/);
if (codespacesMatch) {
const [, codespaceName, , domain] = codespacesMatch;
// Replace the current port (e.g., 3333 for studio) with 3000 for the web app
return `https://${codespaceName}-3000.${domain}`;
}
}
// Third priority: Local development fallback
if (process.env.NODE_ENV === "development") {
return "http://localhost:3000";
}
// Production: must have explicit URL set
throw new Error(
"SANITY_STUDIO_PRESENTATION_URL must be set in production environment",
);
};Allow Server Actions from the Codespaces proxy
Next.js blocks Server Actions requests when the origin header doesn't match the x-forwarded-host header. In Codespaces:
origin:localhost:3000(internal routing)x-forwarded-host:codespace-name-3000.app.github.dev(public URL)
This causes 500 errors: x-forwarded-host header does not match origin header from a forwarded Server Actions request. Aborting the action.
To resolve this, configure Next.js to trust Codespaces proxy domains.
File: next.config.ts or next.config.js
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// ... other config
experimental: {
// ... other config
// Allow Server Actions from Codespaces and other proxied environments
serverActions: {
allowedOrigins: [
"localhost:3000",
"*.app.github.dev",
"*.github.dev",
"*.githubpreview.dev",
],
},
},
// ... rest of config
};
export default nextConfig;What this does
- Tells Next.js to accept Server Actions from Codespaces URLs.
- Maintains security by limiting to specific domains.
- Allows localhost for local development.
Security considerations
- Wildcards are safe for development environments.
- In production, use specific domains only.
- Never use
"*"(all origins) in production.
Let's make sure it works
Start development servers
- Install dependencies.
- Start both development servers.
Access your applications
Get your URLs (Codespaces URLs follow this pattern: https://{CODESPACE_NAME}-{PORT}.app.github.dev)
Frontend
Select port 3000 in the PORTS tab, or construct manually: https://your-codespace-name-3000.app.github.dev
Studio
Select port 3333 in the PORTS tab, or construct manually: https://your-codespace-name-3333.app.github.dev
Verify
Open Studio (port 3333)
- You should see the Sanity Studio interface.
- If you see project ID errors, confirm your
.envfile has the correct project variables.
Open the Presentation Tool
- Click Presentation in the Studio toolbar.
- You should see your frontend loaded in an iframe.
Test live editing
- Edit content in Studio.
- Changes should appear in Presentation.
Troubleshooting
Issue: 502 Bad Gateway on port URLs
Cause
Servers aren't running or ports aren't forwarded correctly.
Solution
# Check if servers are running
lsof -i :3000 # Should show Node.js process
lsof -i :3333 # Should show Node.js process
# Test local access
curl http://localhost:3000 # Should return HTML
curl http://localhost:3333 # Should return HTML
# Verify port visibility
gh codespace ports # Should show both ports as publicIssue: "projectId can only contain only a-z, 0-9 and dashes"
Cause
Environment variables not loaded or .env file missing.
Solution
cd apps/studio
# Check if .env exists
ls -la .env
# Verify project ID is set
cat .env | grep SANITY_STUDIO_PROJECT_ID
# If missing, create .env file with correct valuesIssue: Presentation Tool shows a blank screen
Cause
Frontend URL not configured correctly, or CORS issues.
Solutions
- Check browser console in the presentation iframe (right-click → Inspect).
- Verify CORS origins for your project include
https://YOUR_CODESPACE_NAME-3000.app.github.dev(frontend) and the same URL with port 3333 (Studio). - Check frontend is accessible: open port 3000 directly in a new tab.
- Verify the presentation URL: add
console.log(getPresentationUrl())to debug.
Issue: 500 error "Invalid Server Actions request."
Cause
Origin mismatch between forwarded host and origin headers.
Solution
Verify next.config.ts includes the serverActions.allowedOrigins configuration from the "Allow Server Actions from the Codespaces proxy" section.
Issue: WebSocket connection errors
Cause
This is expected behavior in Codespaces due to the known proxy issues.
Solution
WebSocket errors that appear in the console can be safely ignored. Live editing should still work as expected.
Was this page helpful?
Feedback submitted, thanks for helping us improve the docs. Have you tried these alternatives?