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.

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.
  • 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_NAME with the name of your codespace (the value of $CODESPACE_NAME in 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.dev matches 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_NAME

Why 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=
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=
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 .env file 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 public

Issue: "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 values

Issue: 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?

sanity.ioAll Systems Operational© Sanity 2026
Change site theme