Morphos & Kosmesis are here.Explore
PraxisJS

DevTools

@praxisjs/devtools — in-app developer tools overlay for inspecting signals, component performance, and timeline events. Extensible via plugins.

DevTools

An in-app developer tools panel that appears as an overlay during development. Inspect reactive state, component render metrics, and a chronological event log — without leaving the browser.

npm install @praxisjs/devtools
pnpm add @praxisjs/devtools
yarn add @praxisjs/devtools
bun add @praxisjs/devtools

Setup

Use a dynamic import so DevTools is never included in the production bundle:

// src/main.ts
import { render } from '@praxisjs/runtime'
import { App } from './app'

render(() => <App />, document.getElementById('app')!)

if (import.meta.env.DEV) {
  const { DevTools } = await import('@praxisjs/devtools')
  DevTools.init()
}

A floating button appears in the bottom-right corner of the page. Click it to open the panel.


Built-in panels

Signals

Shows every field and getter decorated with @Debug() and its last 20 values.

import { Component, State } from '@praxisjs/decorators'
import { Debug } from '@praxisjs/devtools'

@Component()
class CartStore extends StatefulComponent {
  @Debug({ label: 'Cart Items' })
  @State()
  items: Product[] = []

  @Debug()
  @State()
  total = 0

  @Debug({ label: 'Has Discount?' })
  get hasDiscount() {
    return this.total > 100
  }
}

The label option controls the display name — if omitted, the field or getter name is used.

Components

Shows components instrumented with @Trace() and their render metrics:

  • Render count and last render duration in ms
  • Mount and unmount timestamps
  • Full lifecycle hook history
import { Trace } from '@praxisjs/devtools'

@Trace()
@Component()
class DataGrid extends StatefulComponent {
  @State() rows: Row[] = []

  render() {
    return <table>{/* ... */}</table>
  }
}

Timeline

A chronological log of the last 200 events across the entire application:

Event typeTriggered by
signal:change@Debug field or getter update
component:render@Trace component render
component:mount@Trace component mount
component:unmount@Trace component unmount
lifecycleIntermediate lifecycle hooks via @Trace
method:call@Debug method invocation

Performance

Profiles the reactive graph. Press Record, use the app, then Stop:

  • Totals: signal writes, effect runs and their time, computed recomputations and their time
  • Per component: how many bindings and effects it ran, their total time and the slowest single run, busiest first

Effects are attributed to the component that was rendering when they were created (and effects created by a re-run inherit that component), so the table answers "which component is doing the work?". Nothing is recorded, and the graph pays nothing, while the profiler is stopped. The panel's own updates are excluded.

The profiler is also available from code and from the browser console:

const { profiler } = DevTools.registry   // or __PRAXIS_DEVTOOLS__.profiler in the console
profiler.start()
// … interact …
profiler.stop()
console.table(profiler.snapshot().owners)

@Debug(options?) reference

Tracks state, props, computed values, and methods in the Signals panel and Timeline, with optional console logging (log) and debugger pauses (break). Imported from @praxisjs/devtools.

See DevTools Decorators for the full reference.


Plugins

DevTools is extensible — each tab is a plugin. You can add new tabs, replace built-in ones, or strip the panel to only what you need.

See DevTools Plugins for the full guide.


Production builds

In a production build (process.env.NODE_ENV === "production") @Debug and @Trace return the member or class exactly as declared, so they cost nothing, and importing them brings neither the registry nor the panel into the bundle. Using a dynamic import for DevTools.init() keeps the panel itself out of the production bundle.

On this page