The Web View
Render pages, canvas, audio and React, Vue, Solid or Angular components beside the console, with optional Tailwind.
Both browser runtimes, Browser and Browser & Node APIs, render into a Web View tile beside the console. The Bun runtime has no page, so it has no Web View.
Show and hide it
A browser-runtime tab draws into a real page you can see and click, right under its output.
document.body.innerHTML = `
<main class="grid min-h-screen place-items-center bg-slate-50">
<button id="counter" class="rounded-xl bg-blue-600 px-6 py-3 text-lg text-white">Clicked 0 times</button>
</main>`;
const button = document.querySelector("#counter") as HTMLButtonElement;
let clicks = 0;
button.addEventListener("click", () => {
clicks++;
button.textContent = `Clicked ${clicks} times`;
});
button;
Toggle the Web View with ⌥⌘W, View → Web View, or the Show Web View and Hide Web View button in the status bar. The setting is per tab, and it comes back after a relaunch. Drag the divider between the output and the Web View to resize them.
What runs in the page
Every run reloads the page, so the DOM starts clean each time; nothing you added on one run is still there on the next.
The page starts empty, with a single <div id="root">. document, canvas, requestAnimationFrame and the rest of the browser’s APIs work, so canvas animation, React, Three.js and Web Audio all run as they would in a browser. Packages you install are bundled into the page for you.
const canvas = document.createElement("canvas");
canvas.width = 200;
canvas.height = 120;
document.getElementById("root")?.append(canvas);
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.fillStyle = "tomato";
ctx.fillRect(20, 20, 160, 80);
}
Logging a page element shows its opening tag, its attributes and how many children it has, and expands to its markup.
Components: React, Vue, Solid and Angular
Export a component and it renders itself in the Web View; the New Tab dialog sets everything up.
Default-export a component, and JSLab renders it into the page for you:
import { useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Hello from React</h1>
<button onClick={() => setCount(count + 1)}>Clicked {count} times</button>
</div>
);
}
- JSLab works out the framework from your imports. The Framework menu in the status bar shows what it found, and lets you pick Auto, React, Solid, Vue, Angular or None. None turns the preview off for that tab.
- JSLab renders the component only when your code hasn’t put anything in
#rootitself. Code that mounts its own app is left alone. - The component needs its framework’s renderer package installed:
react-domfor React,solid-jsfor Solid,vuefor Vue, and@angular/platform-browserfor Angular. If it’s missing, the console names the package to install.
The quickest start is the + button in the tab bar. Choose React, Vue, Solid or Angular, and JSLab sets the tab’s language, runtime and framework, installs the packages, and opens starter code.
- React and Solid tabs use TSX.
- Vue tabs use TSX too: write components with
defineComponentand JSX. The type-only form ofdefinePropsisn’t supported. - Angular tabs use TypeScript, not TSX, and the component has to be
standalone. They need@angular/core,@angular/compiler,@angular/platform-browserandzone.js, which the New Tab dialog installs.
Tailwind
Turn on Tailwind for a browser tab and its utility classes style the page, with nothing to install.
document.body.innerHTML = `
<div class="grid min-h-screen place-items-center bg-gradient-to-br from-sky-100 to-indigo-200">
<div class="rounded-2xl bg-white p-8 shadow-xl">
<h1 class="text-2xl font-bold text-slate-900">Tailwind, no setup</h1>
<p class="mt-2 text-slate-500">Edit a class and watch the page change.</p>
</div>
</div>`;
document.querySelector("h1");
Turn on Tailwind in the status bar and JSLab compiles the Tailwind classes your tab’s code uses into the page. The switch is per tab and works in the browser runtimes only. It’s also Toggle Tailwind in the command palette, and Use Tailwind in the New Tab dialog.
To start new browser tabs with Tailwind on, turn on Tailwind by Default in Settings → Build.
export default function Card() {
return <div className="rounded-lg bg-slate-800 p-6 text-white">Styled with Tailwind</div>;
}
Audio and muting
Canvas, requestAnimationFrame and Web Audio work in the Web View, and a playing tab can be muted.
document.body.innerHTML = '<canvas width="320" height="200" style="background:#0f172a"></canvas>';
const canvas = document.querySelector("canvas") as HTMLCanvasElement;
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
let x = 20;
let dx = 3;
const frame = () => {
x += dx;
if (x < 20 || x > 300) dx = -dx;
ctx.clearRect(0, 0, 320, 200);
ctx.fillStyle = "#f59e0b";
ctx.beginPath();
ctx.arc(x, 100, 18, 0, Math.PI * 2);
ctx.fill();
requestAnimationFrame(frame);
};
frame();
canvas.onclick = () => {
const audio = new AudioContext();
const tone = audio.createOscillator();
tone.connect(audio.destination);
tone.start();
tone.stop(audio.currentTime + 0.2);
};
canvas;
A tab shows a speaker icon in the tab bar while an AudioContext is running or a media element is playing. Click the icon to mute that tab, and click it again to unmute.
- An
OfflineAudioContextrender isn’t tracked. - A live audio context stays
suspendeduntil a user gesture, as the browser’s autoplay policy requires.
Dialogs in the page
alert, confirm and prompt can’t block in the Web View. alert shows a JSLab notice and returns at once, confirm returns false, and prompt returns null. See Bun vs Node for the other differences.