> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blaxel.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up Vite with Blaxel Sandboxes

> Run a Vite React and TypeScript application in a Blaxel Sandbox and expose it securely with a preview URL.

This guide explains how to run a Vite React and TypeScript application inside a Blaxel Sandbox and expose it securely using Blaxel previews.

## Prerequisites

* [Blaxel CLI](../../cli-reference/introduction) installed and authenticated with `bl login`
* Node.js 18 or later
* `@blaxel/core` installed in your project with `npm install @blaxel/core`

## 1. Understand the architecture

Running Vite inside a Blaxel Sandbox requires a few adjustments:

* Vite normally binds to localhost
* Blaxel exposes services through preview URLs
* Vite must accept external connections and all hosts

Configure `vite.config.ts` with `host: "0.0.0.0"` and `allowedHosts: true`, then expose the Vite development server through a [Blaxel preview URL](../../Sandboxes/Preview-url).

## 2. Build the sandbox image

Create a `Dockerfile`:

```dockerfile theme={null}
FROM node:22-alpine

RUN apk update && apk add --no-cache \
  git curl bash \
  && rm -rf /var/cache/apk/*

WORKDIR /app

RUN curl -fsSL https://bun.sh/install | bash \
  && ln -s /root/.bun/bin/bun /usr/local/bin/bun \
  && ln -s /root/.bun/bin/bunx /usr/local/bin/bunx

RUN bun create vite@latest . --template react-ts

# Install patch-package globally first to avoid postinstall script errors
RUN bun install -g patch-package
RUN bun install

COPY --from=ghcr.io/blaxel-ai/sandbox:latest /sandbox-api /usr/local/bin/sandbox-api

COPY ./vite.config.ts /app/vite.config.ts
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
```

## 3. Configure Vite

Create `vite.config.ts` to allow external connections:

```typescript theme={null}
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  server: {
    port: 5173,
    host: "0.0.0.0",
    allowedHosts: true,
  },
});
```

## 4. Create the entrypoint

Create `entrypoint.sh` to start the sandbox API and Vite development server:

```bash theme={null}
#!/bin/sh

export PATH="/usr/local/bin:$PATH"

/usr/local/bin/sandbox-api &

wait_for_port() {
  local port=$1
  local timeout=30
  local count=0

  echo "Waiting for port $port to be available..."
  while ! nc -z localhost "$port"; do
    sleep 1
    count=$((count + 1))
    if [ "$count" -gt "$timeout" ]; then
      echo "Timeout waiting for port $port"
      exit 1
    fi
  done
  echo "Port $port is now available"
}

wait_for_port 8080

echo "Running Vite dev server with Bun..."
curl http://localhost:8080/process \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "name": "dev-server",
    "workingDir": "/app",
    "command": "bun run dev",
    "waitForCompletion": false,
    "restartOnFailure": true,
    "maxRestarts": 25
  }'

wait
```

## 5. Configure and deploy the image

Create `blaxel.toml` beside the `Dockerfile`:

```toml theme={null}
type = "sandbox"
name = "vite-template"

[runtime]
memory = 4096

[[runtime.ports]]
name = "vite-dev"
target = 5173
protocol = "tcp"
```

Deploy the image:

```bash theme={null}
bl deploy
```

## 6. Create or reuse a sandbox

```typescript theme={null}
import { SandboxInstance } from "@blaxel/core";

const sandboxName = "my-vite-sandbox";

const sandbox = await SandboxInstance.createIfNotExists({
  name: sandboxName,
  labels: {
    framework: "vite",
  },
  image: "vite-template:latest",
  memory: 4096,
  ports: [
    { name: "preview", target: 5173, protocol: "HTTP" },
  ],
});
```

## 7. Configure preview response headers

Vite development servers work well with permissive CORS headers when accessed through a preview URL:

```typescript theme={null}
const responseHeaders = {
  "Access-Control-Allow-Origin": "*",
  "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",
};
```

Alternatively, use [custom domains](../../Infrastructure/Custom-domains) to expose previews on your own domain.

## 8. Create the preview and token

Vite runs on port `5173`. Create a private preview for that port:

```typescript theme={null}
const preview = await sandbox.previews.createIfNotExists({
  metadata: { name: "dev-server-preview" },
  spec: {
    responseHeaders,
    public: false,
    port: 5173,
  },
});
```

Generate a token that expires after one day:

```typescript theme={null}
const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24);
const token = await preview.tokens.create(expiresAt);
```

## 9. Start and monitor the development server

If you do not use the entrypoint script, start Vite programmatically:

```typescript theme={null}
async function startDevServer(sandbox: SandboxInstance) {
  console.log("Starting Vite dev server...");
  await sandbox.process.exec({
    name: "dev-server",
    command: "bun run dev",
    workingDir: "/app",
    waitForPorts: [5173],
    restartOnFailure: true,
    maxRestarts: 25,
  });
}
```

Stream its logs:

```typescript theme={null}
const logStream = sandbox.process.streamLogs("dev-server", {
  onLog(log) {
    console.log(log);
  },
});

logStream.close();
```

## 10. Access the Vite application

Once the server is running, open:

```text theme={null}
${preview.spec?.url}?bl_preview_token=${token.value}
```

## 11. Run the complete example

```typescript theme={null}
import { SandboxInstance } from "@blaxel/core";

const sandboxName = "my-vite-sandbox";

const responseHeaders = {
  "Access-Control-Allow-Origin": "*",
  "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",
};

async function startDevServer(sandbox: SandboxInstance) {
  await sandbox.process.exec({
    name: "dev-server",
    command: "bun run dev",
    workingDir: "/app",
    waitForPorts: [5173],
    restartOnFailure: true,
    maxRestarts: 25,
  });
}

async function main() {
  try {
    const sandbox = await SandboxInstance.createIfNotExists({
      name: sandboxName,
      labels: {
        framework: "vite",
      },
      image: "vite-template:latest",
      memory: 4096,
      ports: [
        { name: "preview", target: 5173, protocol: "HTTP" },
      ],
    });

    const preview = await sandbox.previews.createIfNotExists({
      metadata: { name: "preview" },
      spec: {
        responseHeaders,
        public: false,
        port: 5173,
      },
    });

    const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24);
    const token = await preview.tokens.create(expiresAt);

    const processes = await sandbox.process.list();
    if (!processes.find((process) => process.name === "dev-server")) {
      await startDevServer(sandbox);
    }

    const webUrl = `${preview.spec?.url}?bl_preview_token=${token.value}`;
    console.log(`Vite Preview URL: ${webUrl}`);

    const logStream = sandbox.process.streamLogs("dev-server", {
      onLog(log) {
        console.log(log);
      },
    });

    process.on("SIGINT", () => {
      logStream.close();
      process.exit(0);
    });
  } catch (error) {
    console.error("Error:", error);
    process.exit(1);
  }
}

main();
```

This setup runs Vite fully inside a Blaxel Sandbox, securely exposes the development server, supports hot module replacement, and uses Bun for fast dependency installation and development server performance. It is suitable for preview environments, internal demos, and AI-powered coding workflows.

## Resources

<CardGroup cols={2}>
  <Card title="Real-time previews" href="../../Sandboxes/Preview-url">
    Configure public and private preview URLs for sandbox services.
  </Card>

  <Card title="Sandbox images" href="../../Sandboxes/Templates">
    Build and manage reusable Blaxel Sandbox images.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.