Developer guides

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:
    • https://*.app.github.dev
    • https://*.github.dev
    • https://*.githubpreview.dev
  • For each origin:
    • Check "Allow Credentials".
    • Select "Add".

Why wildcards?

  • Each Codespace has a unique name in the URL.
  • Wildcards allow any Codespace to access your Sanity project.
  • Alternative: Add specific Codespace URLs (less flexible).

Security notes

  • These wildcards are safe for development environments.
  • Remove them in production and use specific domains.

Configure port forwardings

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 on port 3000 -> Port Visibility -> Public
  • Right-click on port 3333 -> 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:

Studio environment file

Create apps/studio/.env and add the following environment variables:

How to 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 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

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 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 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

Presentation Mode Shows 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 in Sanity dashboard include *.app.github.dev.
  • 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 Allow Server Actions from Codespaces Proxy above.

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?