Skip to main content
Sometimes you may need to access a running sandbox application and preview the content in real time in a front-end client. This is useful for example to instantly preview React code generated by a codegen AI agent. You can do this via a preview URL that routes to a specific port on your sandbox (e.g. port 3000 for npm run dev). This preview URL can be either public (does not require you to be authenticated to access it) or private (see down below). They will look something like this:
https://tkmu0oj2bf6iuoag6mmlt8.us-pdx-1.preview.bl.run
You can set a custom domain on a preview URL (see down below).

Current limitations of real-time previews

JavaScript module bundlers handle real-time previewing. Here are the key compatibility requirements and limitations:
  • Module bundler must implement ping-pong
  • Webpack has been tested and works
  • Turbopack currently doesn’t work as it doesn’t support ping-pong (see issue raised to Vercel)
  • Blaxel has a 15-minute connection timeout. To maintain previews beyond this limit, ensure your bundler implements automatic reconnection
  • You cannot create a preview on port 80 which is reserved for system

Private preview URLs

When you create a private preview URL a token is required to access the URL. You must include the token as:

Manage preview URLs

Blaxel console

You can create a preview URL for a sandbox from the Blaxel Console, on the overview of a sandbox: Screenshot 2025-05-06 at 10.50.49 PM.png

Blaxel SDK

The Blaxel SDK authenticates with your workspace using credentials from these sources, in priority order:
  1. when running on Blaxel, authentication is handled automatically
  2. variables in your .env file (BL_WORKSPACE and BL_API_KEY, or see this page for other authentication options).
  3. environment variables from your machine
  4. configuration file created locally when you log in through Blaxel CLI (or deploy on Blaxel)
When developing locally, the recommended method is to just log in to your workspace with Blaxel CLI. This allows you to run Blaxel SDK functions that will automatically connect to your workspace without additional setup. When you deploy on Blaxel, this connection persists automatically.When running Blaxel SDK from a remote server that is not Blaxel-hosted, we recommend using environment variables as described in the third option above.
Create and manage a sandbox’s public preview URL:
import { SandboxInstance } from "@blaxel/core";

const sandbox = await SandboxInstance.get("my-sandbox");
// Create public preview
const preview = await sandbox.previews.create({
    metadata: { name: "app-preview" },
    spec: {
        port: 3000,
        public: true,
        responseHeaders: {
            "Access-Control-Allow-Origin": "https://YOUR-DOMAIN",
            "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS, PATCH",
            "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, X-Blaxel-Workspace, X-Blaxel-Preview-Token, X-Blaxel-Authorization",
            "Access-Control-Allow-Credentials": "true",
            "Access-Control-Expose-Headers": "Content-Length, X-Request-Id",
            "Access-Control-Max-Age": "86400",
            "Vary": "Origin"
        }
    }
});

// Get preview URL
const url = preview.spec?.url;
Or create a private preview:
import { SandboxInstance } from "@blaxel/core";

const sandbox = await SandboxInstance.get("my-sandbox");

// Create private preview
const preview = await sandbox.previews.create({
    metadata: { name: "private-preview" },
    spec: {
      port: 3000,
      public: false
    }
});

// Create access token (10 minutes expiry)
const expiresAt = new Date(Date.now() + 10 * 60 * 1000);
const token = await preview.tokens.create(expiresAt);

// How to access the preview with the token
const url = preview.spec?.url;
const response = await fetch(`${url}/health?bl_preview_token=${token.value}`);

Create if not exists

Just like for sandboxes, this helper function either retrieves an existing preview or creates a new one if it doesn’t exist. Blaxel first checks for an existing preview with the provided name and either retrieves it or creates a new one using your specified configuration.
const preview = await sandbox.previews.createIfNotExists({
    metadata: {
        name: "preview-name"
    },
    spec: {
        port: 443,
        public: false
    }
})

Custom domains

To set up a custom domain for your sandbox preview:
  • Register a custom domain to your Blaxel workspace and complete the verification process
  • Use this verified custom domain when creating a new preview:
const preview = await sandbox.previews.create({
    metadata: {
        name: "preview-custom-domain"
    },
    spec: {
        port: 443,
        public: false,
        customDomain:your.custom.domain.dev
    }
})

Troubleshooting

Cannot hot-reload on Webpack preview

If you’re running a Webpack server and can’t connect to it via WebSocket from your client (for hot reloading the preview), the issue is likely that you’re trying to connect to the same port you use in local development—but the preview URL is already pointing to a different port on your sandbox. To fix this, add a conditional in the webSocketURL of your webpack/config.dev.js to handle the different ports:

///... Rest of webpack/config.dev.js

module.exports = {
  devServer: {
    host: "::",
    port: 3000,
    allowedHosts: [".bl.run", ".beamlit.net", "localhost", "127.0.0.1"],
    client: {
      webSocketURL: {
        port: process.env.BL_CLOUD === "true" ? 443 : 3000,
      },
    },
    headers: {
      ...
    },
  },
  /// ... Rest of webpack/config.dev.js