angular-playground.landing Open editor

angular/playground

Angular Playground for Components, Services and Signals

Answer a small Angular question in the time it takes to ask it — a service injected, a signal propagating, an error naming its token, all with the console sitting beside the running app.

loopJIT on save
direal injector
consolebeside the app
outputshareable link
playground.component.ts
import { Component, signal, effect } from '@angular/core'

@Component({ selector: 'demo', template: `
  <button (click)="n.set(n() + 1)">{{ n() }}</button>
` })
export class Demo {
  n = signal(0)
  log = effect(() => console.log('n is', this.n()))
}
JIT 18ms

01 platform

Why Angular questions are best answered by running them

Angular's hard parts are wiring questions — which injector resolved this, why change detection did not fire, what this token is. Dependency injection and signals are the two most searched of them, and neither answers well on paper.

  • Injector scope, demonstrated in seconds

    Move a service from providedIn: 'root' to a component's providers array and watch the constructor fire once per component instead of once per app. That is the whole concept, in one edit.

  • NullInjectorError reproduced deliberately

    Angular's most-searched error is easiest to understand when you cause it on purpose. Remove a provider, read the token in the message, put it back.

  • Signal propagation you can watch

    An effect logging its own dependencies shows exactly when a computed re-evaluates and when it serves a cached value.

  • No ng new between you and the question

    The scaffold wait is the reason small Angular questions get answered by guessing instead of by testing. There is no scaffold here.

02 pipeline

Testing a service, start to finish

Four moves. This is what checking an Angular service costs here, against the several minutes a CLI scaffold costs before the first line of your own code.

  1. Write the service

    An @Injectable class in its own file, with a providedIn scope. Two files, because a service that only exists inside one component is not the thing you are testing.

  2. Inject it

    inject(MyService) in a component field. The injector resolves at construction, and a log in the service constructor tells you how many instances actually exist.

  3. Change the scope

    Move the provider between the root and the component and watch the instance count change. This is the experiment that makes DI hierarchies concrete.

  4. Share the result

    Send the link. Whoever opens it gets the same providers, the same log output and the same reproduction — what an issue thread actually needs.

03 features

What the playground gives an Angular experiment

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

  • The real injector

    Provider scopes, InjectionToken, multi-providers and inject() all behave as Angular implements them, so what you learn transfers to a production app unchanged.

  • Angular's own dev-mode errors

    NullInjectorError, ExpressionChangedAfterItHasBeenChecked and template binding errors arrive with Angular's wording, which is what makes them searchable.

  • Signals and the control flow

    signal, computed, effect and the @if / @for blocks compile as they do locally, so the reactivity you test is the reactivity you will ship.

  • RxJS operators in full

    Subjects, operators and the async pipe work against the real library, so a marble-diagram question can be answered by running it.

  • Real npm packages

    Angular Material, NgRx or a date library install and import by name, because a mock answers a question you did not ask.

  • A link that reproduces

    Share the project and it opens as you left it — usable on a GitHub issue rather than as a screenshot.

04 deep dive

Watching a signal propagate

Angular signals replaced Zone.js checking everything with values that notify their own dependents. That is a mental-model change, and models are learned by watching rather than reading.

effect shows you its own dependencies. Log inside one and the console tells you exactly which signal change triggered the re-run — including the ones you did not expect.

computed caching is observable. Read a computed twice with a log inside it; it fires once. The cache stops being a claim and becomes a thing you saw.

Zoneless change detection changes the timing. Without Zone.js patching async APIs, an update happens because a signal was set rather than because a timer fired. Watching that is how the difference stops being abstract.

toSignal bridges the old model. An existing Observable becomes a signal without rewriting the stream, which is how a real migration proceeds one piece at a time.

effect
dependency tracking
computed
cached, observable
change detection
zoneless
bridge
toSignal
reactive.ts
const n = signal(1)
const doubled = computed(() => {
  console.log('computed ran')
  return n() * 2
})

effect(() => console.log('effect', doubled()))

05 workflow

An injection failure, and the fix

One real sequence, from opening the tab to understanding an error. The console sits beside the app, so nothing here required opening devtools.

session
  • open → Angular playground
  • JIT compile 18ms · bootstrapped
  • [log] UserService constructed
  • edit → remove providedIn
  • NullInjectorError: No provider for UserService!
  • R3Injector.get — user-card.component.ts:9
  • add to providers → save
  • recompiled · 1 instance per component
  • The token is named

    Angular's injector errors say which token failed to resolve, which turns a vague failure into a one-line fix once you know where to look.

  • Instance counts are visible

    A log in a constructor is the cheapest possible instrumentation for provider scope, and it answers the question no diagram does.

  • Recovery is one save

    Fix the provider and save. The app re-bootstraps in place; there is no dev server to restart.

06 deep dive

When injection or change detection goes wrong

Two categories cover most Angular debugging, and both are far easier to see than to reason about.

Injection errors point at a token and a file. NullInjectorError names what could not be resolved and where it was requested, so the search starts in the right place.

Change detection surprises have a name. ExpressionChangedAfterItHasBeenChecked is Angular telling you a value moved during the check, and reproducing it deliberately is the fastest way to stop fearing it.

Angular DevTools attaches normally. The browser extension inspects the real component tree and the injector graph, because the preview is a genuine Angular application rather than a picture of one.

injection
token + file
change detection
named errors
devtools
Angular DevTools works
restart
never needed
output
NullInjectorError: R3InjectorError(Standalone[Demo])[UserService]:
  NullInjectorError: No provider for UserService!

  requested by  user-card.component.ts:9:23

07 ecosystem

One project, several frameworks

An Angular component can sit beside a .vue file and a Python script; the compiler is chosen per file, not 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 understands

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 Angular playground — common questions

Questions about the fast loop specifically.

How is this different from a StackBlitz Angular starter?

StackBlitz runs a real Node process in the browser, which buys you the CLI and a dev server. This trades those away for an instant start and a compiler that runs entirely on your device — no container to boot, and it keeps working offline.

Why does my service construct more than once?

Provider scope. providedIn: 'root' gives one instance for the whole application; the same service listed in a component's providers array gives one per component instance. Put a log in the constructor and the answer becomes obvious in a few seconds.

Can I reproduce a NullInjectorError deliberately?

Yes, and it is one of the better uses for a playground. Remove a provider, read the token Angular names in the message, then put it back. Causing the error on purpose is how it stops being frightening.

Do signals and effects work here?

Yes. signal, computed and effect run against Angular's own reactivity, and the built-in @if and @for blocks read them in templates directly. An effect logging its dependencies is the fastest way to see when things re-evaluate.

Does RxJS still work if I prefer Observables?

Fully. Operators, subjects and the async pipe behave as they do locally, and toSignal and toObservable let you mix both models in the same component rather than committing to one.

Does Angular DevTools work?

Yes. The browser extension attaches to the preview and inspects the real component tree and injector graph, because the preview is a genuine Angular application.

Can I test a component that uses the router?

Yes. Install @angular/router, provide it in the bootstrap and navigate in the preview. Route guards and resolvers run as they would in a local project.

Is there a limit on how long a playground lasts?

No. Projects live in your browser rather than on a server with a retention policy, so an experiment you left open last month is still there.

Does the playground work if I lose my connection?

Once loaded, yes. XCODX installs as a Progressive Web App and the JIT compiler runs on your device, so editing and compiling keep working offline. 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.