Introducing GyosJS: Reactive HTML and MPA Boost for Server-Rendered Apps
Server-rendered applications are still a practical way to build software. The backend owns routing, validation, authorization, and HTML generation. A browser can follow a link, submit a form, receive a redirect, and rend
Server-rendered applications are still a practical way to build software. The backend owns routing, validation, authorization, and HTML generation. A browser can follow a link, submit a form, receive a redirect, and render the result without requiring a separate client application architecture.
The awkward part often starts when that application needs more interaction. A filter should update as someone types. A modal needs local state. A form needs conditional fields and computed values. Page transitions should feel less abrupt. A small request for better UX can become a decision about adopting a component framework, client-side routing, an API layer, and a second model of application state.
GyosJS is an attempt to keep that decision smaller.
It is a JavaScript library for adding reactive behavior directly to server-rendered HTML. It also includes MPA Boost, a navigation layer that can intercept ordinary links and forms, fetch the next HTML document, and update the relevant part of the current page. The server remains responsible for routes and responses. GyosJS enhances the document the server already produces.
Version 0.1.0 is the first public release. It is early software, and this introduction is meant to explain what it does, where it may fit, and what still needs to be proven through real applications.
Reactive HTML close to the markup
The smallest GyosJS unit is a scope. A g-scope attribute gives a section of HTML local reactive state. Expressions and directives inside that section read and update the state.
<div g-scope="{ quantity: 1, price: 24 }">
<label>
Quantity
<input type="number" min="1" g-model.number="quantity">
</label>
<p>Total: ${quantity * price}</p>
<button type="button" @click="quantity++">Add one</button>
</div>
There is no component build step in this example. g-model keeps the input and scope value synchronized, {quantity * price} updates when a dependency changes, and @click evaluates the event expression in the same scope.
For larger behavior, a scope can be registered in JavaScript instead of written inline:
Gyos.scope('ProductEditor', {
step: 1,
price: 0,
stock: 0,
get canContinue() {
return this.price >= 0 && this.stock >= 0;
},
next() {
if (this.canContinue) this.step++;
}
});
<form g-scope="ProductEditor">
<section *if="step === 1">...</section>
<section *if="step === 2">...</section>
<button type="button" @click="next" :disabled="!canContinue">
Continue
</button>
</form>
The intent is not to move the entire application into the browser. A scope should usually describe one interactive concern: a product form, search controls, a disclosure, a notification tray, or a modal. Backend-rendered values can initialize it, and normal form fields still carry data back to the server.
GyosJS includes directives for conditional rendering, iteration, text and HTML output, attributes, events, two-way form binding, transitions, references, context, and validation-oriented behavior. It also exposes signals, effects, stores, and lifecycle hooks for cases that need JavaScript APIs rather than markup alone.
MPA Boost, not a client-side router
Reactive widgets solve only part of the problem. In a traditional multi-page application, moving between routes still replaces the whole document. That behavior is reliable, but it can interrupt persistent UI and make small transitions feel heavier than necessary.
MPA Boost enhances the browser navigation model without replacing backend routing:
<body g-boost>
<main id="app" g-outlet g-snapshot>
<nav>
<a href="/products">Products</a>
<a href="/orders">Orders</a>
</nav>
<!-- Server-rendered page content -->
</main>
</body>
For eligible same-origin navigation, GyosJS requests the next page, parses the returned HTML, updates the outlet, synchronizes supported document metadata and scripts, mounts new scopes, and writes browser history. A direct request or refresh still reaches the same backend route and returns a complete HTML page.
This distinction matters. GyosJS does not introduce a second route table in JavaScript. It does not require JSON responses for normal navigation. Redirects, validation responses, sessions, middleware, and backend templates remain part of the application's existing request lifecycle.
The router also supports narrower updates. A link can select a target and choose how returned content is swapped:
<a href="/products?status=low-stock"
g-target="#product-results"
g-swap="inner">
Low stock
</a>
<section id="product-results">
<!-- The server returns the matching region -->
</section>
Append and prepend swaps can support patterns such as load more. Morph swaps can preserve compatible DOM state while reconciling similar markup. These are tools for specific regions, not a requirement to turn every response into a fragment endpoint.
Some UI should survive navigation rather than be recreated. A stable g-persist key tells the router to carry the existing DOM node into the next document:
<aside g-persist="ops-scratchpad" g-scope="Scratchpad">
<textarea g-model="notes"></textarea>
<span>{elapsedLabel}</span>
</aside>
That is useful for a media player, timer, draft, or another intentionally long-lived island. It should be used selectively because persisted DOM has a longer lifecycle than normal page content.
Enhancement also needs an escape hatch. Individual links and forms can opt out when native browser behavior is safer, and features such as file downloads, external destinations, or application-specific responses should not be forced through the boosted path. Pages should begin with meaningful links, form actions, names, and server responses; GyosJS can then improve their behavior. This does not make every reactive interaction functional without JavaScript, but it keeps the underlying request model understandable and gives important navigation and submission flows a conventional fallback.
That boundary is part of the design rather than an incidental compatibility feature. Network failures, unexpected response shapes, duplicate element identifiers, and scripts with page-specific side effects can all complicate partial navigation. MPA Boost provides lifecycle and script controls, but an application still has to define which regions are safe to swap and which actions should remain native. The reference demo is intended to make those decisions visible instead of presenting boosted navigation as automatic in every case.
The Inventory Desk journey
The reference application for GyosJS is a small Laravel project called Inventory Desk. It is not intended to become a separate product. Its purpose is to force the library through one realistic server-rendered workflow rather than a collection of isolated examples.
The journey is straightforward:
- Open a product list rendered by Laravel.
- Search, filter, and sort through GET parameters that remain visible and bookmarkable in the URL.
- Load additional rows without replacing the surrounding page.
- Open a server-rendered product detail in a quick-view modal.
- Move into a multi-step edit form with reactive local behavior.
- Submit a normal POST request and let Laravel perform authoritative validation.
- Render validation errors with old input, or redirect after a successful update.
- Use browser back and forward navigation and verify that URL, snapshot, scroll, and page state remain coherent.
- Keep an operations scratchpad alive with
g-persistwhile the user moves between routes.
This journey is deliberately ordinary. CRUD, filters, validation, redirects, history, and notifications are where an HTML navigation library has to cooperate with a real backend. A polished counter demo cannot expose the same lifecycle mistakes.
Laravel remains responsible for models, queries, validation, sessions, redirects, and complete HTML responses. GyosJS handles local form interactions, targeted updates, boosted navigation, and the persistent widget. The browser suite also disables JavaScript and repeats the essential links and forms where progressive enhancement is expected.
That browser journey has already exposed a real router defect: skipped View Transition promises could surface as unhandled page errors when navigation was superseded. The fix was made in the GyosJS router with a regression test rather than hidden inside the Laravel app. This is the main reason Inventory Desk exists: failures found at application boundaries should become library-level tests.
Where GyosJS may fit
GyosJS is aimed at applications that already benefit from server rendering and need a moderate amount of client behavior. Laravel, Rails, PHP MVC projects, backend dashboards, internal tools, and content-oriented sites are natural candidates. It can also fit an existing MPA where rewriting routes and templates as a SPA would add more architecture than the product needs.
It is probably not the right choice for an application whose primary model is a large client-side state graph, offline synchronization, extensive client routing, or a mature component ecosystem. React, Vue, Svelte, and similar frameworks solve those problems with established tools and communities. GyosJS is not trying to replace them.
There are also cases where plain JavaScript is enough. A single menu or dialog does not require a reactive library. GyosJS becomes more useful when several HTML-first interactions need consistent scoping, cleanup, binding, and navigation behavior.
The useful comparison is therefore not βCan GyosJS build everything?β It is βCan this application keep its backend-rendered architecture while gaining the interactions it actually needs?β
Installation and current status
GyosJS can be installed from npm:
npm install gyosjs
The core entry gives explicit control over registration and mounting:
import Gyos from 'gyosjs';
Gyos.scope('Counter', {
count: 0,
increment() {
this.count++;
}
});
Gyos.ready(() => Gyos.mountAll());
The auto entry initializes common browser behavior:
import 'gyosjs/auto';
A browser-ready CDN build is also distributed for projects that do not use a bundler. Exact setup instructions and the current package paths are maintained in the documentation rather than duplicated here.
Version 0.1.0 should be treated as an early release. The project has unit and browser coverage for its core behavior, but it has not yet accumulated broad production use. APIs may need refinement as more applications exercise unusual DOM, form, script, and history combinations. Reports with small reproductions are more useful at this stage than assumptions about maturity.
The immediate work remains practical: run Inventory Desk publicly, expand the browser matrix, document lifecycle boundaries, and fix issues discovered under those constraints. Additional features should follow demonstrated needs rather than expanding the directive list for its own sake.
Try it and evaluate the fit
The GyosJS documentation covers scopes, template syntax, reactivity, forms, lifecycle behavior, MPA Boost, and distribution options. The Inventory Desk demo exercises those boundaries in Laravel. The source repository contains the runtime, tests, and development examples. The npm package provides the current release for installation.
GyosJS exists for a specific middle ground: more structure than scattered DOM scripts, less architectural change than adopting a client-rendered application. Whether that middle ground is useful should be decided by real server-rendered workflows. Inventory Desk is one concrete test of that choice.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.