PawaJs

Reactive Web Runtime.

HTML++

Declarative Reactivity

The PawaJS runtime breathes life into standard HTML. Manage state and logic directly in your templates with attributes, eliminating the need for boilerplate-heavy JavaScript frameworks.

html

                 
<div state-count="0">
    <h3 class="text-xl">@{count.value}</h3>
    <span if="count.value === 0">Zero</span>
    <span else-if="count.value > 0">Positive</span>
    <span else="">Negative</span>
    <button on-click="count.value++">+</button>
</div> 

        

0

Neutral

JavaScript++

Unified Component Logic

Scale your logic with a clean, hook-based API. PawaJS provides universal state management that feels familiar but is optimized for high-speed runtime execution.

js

                 
import {$state, useInsert} from "pawajs";

export const MyCounter=()=>{
    const count=$state(0);
    useInsert({ count })

   return html` 
    <div>
        <d-text>@{count.value}</d-text>
        <button on-click="count.value++">
            Increment
        </button>
    </div>
    `
} 

        

Unified Runtime

Server & Browser Continuity

Build once, render anywhere. PawaJS is a runtime designed for seamless continuity, enabling robust server-side rendering that instantly hydrates for dynamic client-side interactions.

html

                 
<!-- Same component, zero changes -->
<my-counter></my-counter> 

        

Engineered for Runtime Performance

PawaJS is a specialized runtime, not a framework. It provides the essential bridge between static server rendering and dynamic browser interactivity.

Resumable SSR
Zero Hydration overhead
Unlike standard hydration, PawaJS "resumes" from where the server left off. No re-executing components on page load, leading to instant Time to Interactive.
Blazing Fast
No Virtual DOM
By updating the real DOM directly through a fine-grained reactive system, PawaJS avoids the memory and CPU overhead of virtual tree comparisons.
15.5kb Runtime
Minified + Gzipped
PawaJS delivers a lightweight footprint for heavy-weight performance, ensuring your application stays lean and responsive on any device.
Isomorphic API
Unified code execution
Write your logic once. The same code patterns handle server-side rendering and client-side updates, reducing bugs and context switching.

Performance Benchmark

Experience the radical efficiency of a runtime that doesn't re-run your code. PawaJS eliminates the "Hydration Gap" found in traditional frameworks.

Time to Interactive (TTI) Resumability Wins
150ms
850ms
PawaJS (Instant Resumption) Standard Frameworks (Hydration)
Memory Overhead -80% Memory
By removing the Virtual DOM and component tree duplication, PawaJS reduces runtime memory pressure significantly.

15.5KB

Zero Hydration.

Your application is interactive before the browser finishes parsing the framework of the past.

How PawaJS Works: Resumable SSR

PawaJS redefines Server-Side Rendering by eliminating the "Hydration Tax." Understand the fundamental difference that makes your applications instantly interactive.

Traditional Hydration
The "Hydration Tax" in frameworks
1
Server renders HTML. Browser receives static HTML and displays it. (Contentful Paint)
2
Browser downloads and executes JavaScript bundles.
3
Framework re-renders the entire application in the browser, rebuilding the Virtual DOM and attaching event listeners. (Hydration)
4
Application becomes interactive. This re-rendering can cause a "hydration mismatch" and performance overhead.
PawaJS Resumption
Instant interactivity with zero overhead
1
Server renders HTML. Browser receives static HTML and displays it.
2
PawaJS runtime "resumes" execution directly from the server-generated state. No re-rendering or Virtual DOM diffing.
3
Event listeners are attached directly to the existing DOM elements.
4
Application becomes interactive almost instantly, with minimal JavaScript execution.
Continuity Rendering Model (CRM)

The Anatomy of Continuity

PawaJS embeds lightweight markers directly into the HTML. These markers act as "save points," allowing the client-side runtime to pick up exactly where the server left off — without re-evaluating logic or rebuilding the DOM.

  • c-

    Resumption Prefixes

    Attributes like c-c- (components), c-$- (state), c-at- (reactive attributes) and c-t (text nodes) tell the runtime exactly what needs reactivity.

  • <!--

    Serialized Comments

    HTML comments carry component names, unique IDs, and base64-encoded initial state — maintaining an invisible bridge between server and client.

  • p:c

    The p:c Manifest

    Each element carries a semicolon-delimited manifest of exactly which attributes need resumption — enabling O(1) lookup rather than full attribute scanning.

  • tpl

    Branch Store Templates

    All conditional branches travel as inert <template p:store> elements — client toggles state without a server roundtrip.

html

                 
<!-- Server-Rendered HTML with CRM Protocol -->
<div id="app">

  <!-- Component boundary comment:
       format: component+id+Name+base64(state) -->
  <!--component+a1b2c3+MyCounter+eyJjaGlsZHJlbiI6IiIsInByb3BzIjp7ImNvdW50IjowfX0=-->

  <!-- p:c manifest: semicolon-delimited list
       of attributes needing client resumption -->
  <div p:c="c-c-mycounter-a1b2c3;c-$-count;c-at-class;c-t;" c-c-mycounter-a1b2c3="a1b2c3" c-$-count="0" c-at-class="@{count.value === 0 ? 'neutral' : count.value > 0 ? 'increasing' : 'decreasing'}" class="counter-ui neutral">
    <!-- c-t: flags this text node has @{} expression -->
    <h3 p:c="c-t" c-t="">
      0
    </h3>

    <!-- Static elements need no resumption markers -->
    <button on-click="increment()">+</button>
    <button on-click="decrement()">-</button>

    <!-- p:store: all if/else branches travel
         as inert HTML — no server roundtrip needed -->
    <template p:store="" p:store-if="b3c4d5">
      <p if="count.value > 0" class="text-green-500">Increasing</p>
      <p else-if="count.value < 0" class="text-red-500">Decreasing</p>
      <p else="" class="text-muted-foreground">Neutral</p>
    </template>

    <!-- Active branch rendered by server -->
    <!--condition(count.value > 0)@-$@-$@b3c4d5-->
    <p class="text-muted-foreground">Neutral</p>
    <!--end condition(count.value > 0)@-$@-$@b3c4d5-->

  </div>

  <!--end component+a1b2c3+MyCounter-->

</div>