svelte-playground.landing Open editor

svelte/playground

Svelte Playground for Runes, Transitions and Components

Answer a small Svelte question in the time it takes to ask it — a rune recomputing, a transition playing, a style refusing to leak — with the console sitting beside the running component.

loopcompile on save
staterunes
consolebeside the app
outputshareable link
Demo.svelte
<script>
  let n = $state(0)
  let big = $derived(n > 5)
  $effect(() => console.log('n is', n))
</script>

<button onclick={() => n++}>{n}</button>
{#if big}<p>past five</p>{/if}
compiled 5ms

01 platform

Why runes are best understood by running them

The gap between Svelte 4's implicit reactivity and Svelte 5's explicit runes is where nearly all the confusion lives, and it closes faster by watching than by reading.

  • $derived recomputes, it is never assigned

    That single sentence is the whole migration, and it stops being abstract the second you try to assign to one and read the compiler's objection.

  • Deep proxying is visible

    $state proxies nested objects so mutation is tracked. Mutate a field two levels down, watch it update, then try the same with $state.raw and watch it not.

  • $effect shows its own dependencies

    Log inside one and the console names which change re-ran it — including the dependency you did not realise you had read.

  • Transitions are one import away

    svelte/transition needs nothing installed, so trying fly against fade against a spring is three edits rather than three package decisions.

02 pipeline

Testing a rune, start to finish

Four moves. This is what checking a reactivity question costs here, against the minutes a local scaffold costs before the first line of your own code.

  1. Declare the state

    let x = $state(...) in a component, or in a .svelte.ts module if the question is about sharing it. Two files the moment the question involves more than one component.

  2. Derive from it

    Add a $derived that reads it and a $effect that logs. Those two lines are the instrumentation for almost every reactivity question worth asking.

  3. Provoke it

    Mutate deeply, reassign wholesale, swap $state for $state.raw. Each variant prints its own trace beside the running component, so cause and effect stay adjacent.

  4. Share the trace

    Send the link. Whoever opens it gets the same runes, the same logs and the same reproduction — which is what an issue thread actually needs.

03 features

What the playground gives a Svelte experiment

Everything below matters for testing Svelte behavior specifically, rather than for building an application.

  • Svelte 5 compiled on save

    Single-digit milliseconds for a component. The compiler runs on your device, so there is no container to boot and no queue to wait in.

  • Runes in modules too

    .svelte.ts files compile here, so a question about sharing state between components is testable rather than theoretical.

  • Transitions with no install

    svelte/transition and svelte/motion ship with the framework, so comparing fly to spring is an edit rather than a dependency decision.

  • Scoped styles, provably

    Write two components that both style .card and watch neither leak. The compiler's scoping attribute is visible in the rendered output.

  • Console beside the component

    console.log from an $effect lands where you can read it, in the order the compiler actually ran things.

  • A link that reproduces

    Share the project and it opens as you left it, which is what makes it usable on a GitHub issue rather than as a screenshot.

04 deep dive

Watching a derived value recompute

Most Svelte 5 questions reduce to one thing: what re-ran, and why. An instant compile loop answers that by demonstration.

Put a log inside $derived and read it twice. It runs once. The caching stops being a claim in the docs and becomes something you watched happen.

Mutate deep, then reassign. $state proxies nested objects, so obj.a.b = 1 is tracked. Swap to $state.raw and the same mutation goes unnoticed until you replace the whole object.

$effect runs after the DOM updates. That ordering matters when an effect reads a node, and the only reliable way to build the intuition is to log on both sides of it.

Untracked reads exist for a reason. Reading a value without creating a dependency is occasionally what you want, and seeing an effect stop re-running is how that becomes obvious.

derived
cached, recomputed
state
deep proxy
raw
reassignment only
effect
after DOM update
Reactive.svelte
<script>
  let items = $state([{ n: 1 }])

  let total = $derived.by(() => {
    console.log('recomputed')
    return items.reduce((s, i) => s + i.n, 0)
  })
</script>

05 workflow

A reactivity surprise, and the fix

One real sequence, from opening the tab to understanding why something did not update. The console sits beside the component, so nothing here needed devtools.

session
  • open → Svelte playground
  • compile 5ms · mounted
  • [log] recomputed · total 1
  • edit → $state.raw(items)
  • click → mutate items[0].n
  • no recompute — raw tracks reassignment only
  • items = [...items] → save
  • recomputed · total 2
  • The compiler is fast enough to iterate

    A five-millisecond compile means trying four variants costs less than reading one paragraph about which to pick.

  • Nothing hides in a build step

    There is no bundler between the file and the output, so what you see running is what the compiler produced from what you wrote.

  • Recovery is one save

    Change the line and save. The component swaps in place; there is no dev server to restart.

06 deep dive

When state changes and nothing moves

Almost every Svelte 5 bug report is this shape, and almost all of them have one of three causes.

Something was destructured out of reactivity. Pulling a value out of a $state object gives you the value, not the binding. The fix is to read it where you use it.

$state.raw was the wrong choice. Raw state only notices reassignment, which is exactly right for a large array you always replace and exactly wrong for one you mutate.

A $derived was assigned to. Derived values are computed rather than set. The compiler says so, and reproducing the error once is faster than remembering the rule.

destructuring
breaks the binding
raw
reassignment only
derived
never assigned
restart
never needed
Broken.svelte
<script>
  let user = $state({ name: 'Ada' })

  // reads once — not reactive
  const { name } = user

  // reactive — read at use site
  // {user.name}
</script>

07 ecosystem

A project that mixes compilers

A .svelte file can sit beside a .vue one and a Python script; XCODX picks the compiler per file rather than per project.

  • React
  • Vue
  • Angular
  • Svelte
  • SolidJS
  • Lit
  • HTMX
  • Vite
  • TypeScript
  • JavaScript
  • HTML
  • CSS
  • SCSS
  • Less
  • Pug
  • Tailwind
  • Bootstrap
  • Vuetify
  • Ant Design
  • shadcn/ui
  • Phaser
  • Node.js
  • Python
  • Java
  • C++
  • C#
  • Go
  • Rust
  • Ruby
  • PHP
  • Kotlin
  • Swift
  • SQL
  • Bash

08 languages

Every language the editor knows

Highlighting and IntelliSense cover 70+ languages. These are the ones XCODX also compiles or executes for you.

Compiled in your browser11

Zero build step. Transform runs on a Web Worker, so typing never blocks.

  • JSX
  • TSX
  • TypeScript
  • Vue SFC
  • Svelte
  • Angular (JIT)
  • SolidJS
  • SCSS
  • Sass
  • Less
  • Pug

Rendered natively8

Straight to the live preview, no transform in the way.

  • HTML
  • CSS
  • JavaScript
  • JSON
  • Markdown
  • Jupyter (.ipynb)
  • SVG
  • YAML

Executed on demand22

70+ languages run through a self-hosted Piston engine. stdout, stderr, images and tracebacks land in the console.

  • Python
  • Java
  • C
  • C++
  • C#
  • Go
  • Rust
  • Ruby
  • PHP
  • Kotlin
  • Swift
  • Lua
  • Scala
  • Perl
  • Haskell
  • Elixir
  • Dart
  • R
  • Julia
  • SQLite
  • Bash
  • PowerShell

09 questions

Using the Svelte playground — common questions

Questions about the fast loop specifically.

How does this compare to the REPL on svelte.dev?

The official REPL is built to teach the language and is very good at it. This keeps the same instant compile but adds a file tree, npm packages and Git, so an experiment that grows past one component does not have to move somewhere else.

Why did my state change without the UI updating?

Usually destructuring. Pulling a value out of a $state object copies it rather than keeping the binding, so the copy never changes. Read the property where you use it instead, and the reactivity comes back.

When should I use $state.raw instead of $state?

When the value is large and you always replace it wholesale rather than mutating into it — a big table of rows, say. Deep proxying costs something on those, and raw state skips it by tracking reassignment only.

Can I test state shared between components?

Yes, and it is one of the better reasons to use a multi-file playground. Declare $state in a .svelte.ts module, export it, import it in two components, and watch both update without a store or a subscription.

Do transitions and springs work?

Yes, with nothing installed. svelte/transition and svelte/motion ship with the framework, so comparing fly against fade against a spring is three edits in the same tab.

Can I check that scoped styles really scope?

Yes, and it is worth doing once. Write two components that both style .card, look at the rendered output, and the compiler's per-component attribute is right there in the markup.

Does Svelte 4 syntax still compile?

Yes. Svelte 5 kept the older reactive syntax working, so a component using export let and $: labels compiles alongside one using runes. That is what makes an incremental migration testable here.

Does the playground work offline?

Once loaded, yes. XCODX installs as a Progressive Web App and the Svelte compiler runs on your device, so editing and compiling keep working with no connection. Only installing a new package needs the network.

Do I need to install anything to use XCODX Studio?

No. XCODX Studio is a browser-based IDE — the editor, the compilers and the preview all run client-side. There is no download, no package manager to set up and no configuration file to write. Opening the editor is the whole setup step.

Where is my code stored?

In your own browser. Projects, file history and Git objects live in local storage on your device and are never uploaded to a server. That also means XCODX works offline: it is a Progressive Web App, so you can install it and keep coding with no connection.

Is XCODX Studio free?

Yes. The editor, the live preview, the 70+ language runtimes, npm installs, Git and the integrated terminal are all free to use.

Can I use XCODX on a tablet or Chromebook?

Yes. The editor is a responsive web app, so a Chromebook, an iPad or an Android tablet gets the same compilers and the same preview a laptop does — there is no desktop-only build, because there is no build to install at all. The layout adapts to the screen rather than shrinking a desktop UI into it.

Can I export a project or move it to my own machine?

Yes. Export the whole project as a zip with its folder structure intact and open it locally, or commit it and push. It is ordinary source on disk from that point on — no proprietary format and no lock-in, which is also why importing an existing repository from GitHub works the same way in reverse.