The full-stack framework
backend Java · frontend JavaScript / CSS / HTML

Backend in pure Java on Vert.x — HTTP, WebSocket, sessions. Frontend in JavaScript / CSS / HTML — native ES6 modules, no build step, no node_modules, no virtual DOM. Follows the React component pattern: each section of the page is driven by an ES6 controller class, without the VDOM or the transpiler. Full test coverage from Java unit tests to browser end-to-end. Designed for AI-assisted development with plain Flex CSS.

Get started → Browse modules

Built for AI-assisted development — without losing control

AI coding assistants are most useful when they can read the entire stack in one pass and generate code that a human can immediately understand and modify. Yoja is designed around that constraint: plain Java on the backend, native ES6 on the frontend, strict file separation, no hidden conventions. An AI can reason about the full application from source alone — no annotation processors to reverse-engineer, no build output to interpret, no framework magic to guess.

The real payoff is in the critical moments. When a CSS layout breaks, when a JavaScript interaction misbehaves, when an AI gets stuck and keeps regenerating the same wrong answer — you can step in, read the code directly, fix it in one file, and move on. No transpiler output to untangle, no generated class names to trace, no component framework layered on top. The code an AI writes is the code that runs, and it stays readable enough that a human can always take back the wheel.

A philosophy of restraint

Yoja was born from an obsession: build the smallest possible framework that still covers everything a real web application needs. No transpilation, no browser extensions to debug, no intermediate layers — only what browsers already speak natively. The frontend dynamises and structures pages; the backend calculates; CSS with Flexbox handles presentation simply and efficiently. Each concern stays in its own language, its own file, its own layer.

This framework is also a craft project — somewhere between a functional piece of software and something made with care. Clean object paradigms in both Java and JavaScript, strict separation of languages, HTML for structure, CSS for presentation, simple but effective principles. An anti-framework, in a sense: its ambition is not to grow, but to stabilise. The older it gets, the simpler and more reliable it becomes — because it only ever answered the primary, recurring needs of a web application. Built to help a developer and an AI understand each other, now and in the future.

Why Yoja

A small set of opinionated modules that compose into a real application without forcing you to learn a build system.

React pattern, without VDOM

Same component-driven pattern as React — each section has its own controller class — but on native browser APIs. yw-controler, yw-css, yw-language declared in HTML, hydrated from ES6 classes. Direct DOM, no diffing overhead.

Lightweight, fast & maintainable

A small set of focused modules with no hidden magic. Each module does one thing, has a minimal surface, and composes cleanly with the others. Fast to start, easy to reason about, straightforward to upgrade.

Tests: back to front

One test framework from Java unit tests to full browser end-to-end. yoja-selenium boots the real server, drives a browser, runs JS modules as steps — all from a JUnit 6 @TestFactory.

Designed for AI

Plain Java and native ES6 — no transpiler output, no generated boilerplate. Code produced by an AI assistant runs as-is; the framework's explicit conventions give models a consistent surface to read, generate and modify.

Built on Vert.x

Non-blocking I/O, native HTTP/2, and a tested event-loop runtime. Yoja wraps the parts you need with a small, fluent surface.

No build toolchain

Gradle on the JVM, native ES modules in the browser. Drop a JS file, reload the page. No bundler, no transpiler, no source maps to chase.

Sessions & sockets

Cookie-bound sessions, broadcast-ready WebSocket endpoints, jar-served or folder-served resources — all from the same router.

Flex CSS

Each section carries its own scoped stylesheet via yw-css, isolated in a Shadow DOM. Write plain CSS with Flexbox — no CSS framework required, no class-name collisions, no specificity wars between sections.

Reverse-proxy included

Route by host + path to one or many upstreams, with load-balancing, SSL termination, and a live admin API to swap rules at runtime.

Hello, Yoja

Source code on GitHub →

Hello.java
import com.easygoingapi.yoja.core.http.HttpMethod;
import com.easygoingapi.yoja.http.server.HttpRouter;
import com.easygoingapi.yoja.http.server.HttpServer;
import com.easygoingapi.yoja.http.server.WebApp;

public class Hello {
    public static void main(String[] args) {
        HttpRouter router = HttpRouter.builder()
            .contentType("js", "application/javascript")
            .contentType("html", "text/html")
            .webResource(WebApp.of(WebApp.Type.jar, "com.easygoingapi.yoja.web", "/yoja"), "/*")
            .webResource(WebApp.jar("blueprint"), "/*")
            .webService(HttpMethod.GET, "/hello", r ->
                r.response().send("hello, yoja"))
            .build();
        HttpServer.builder(router, 9090)
                  .start()
                  .onSuccess(server -> System.out.println("http://localhost:" + server.port()));
    }
}
index.html
<!doctype html>
<html>
<head>
  <script type="module"
          src="/yoja/YojaWeb-1.0.0.js"></script>
</head>
<body yw-controler="./HelloControler.js">
  <p class="message"></p>
</body>
</html>
HelloControler.js
export default class HelloControler {

    constructor(section) {
        section.pageReady(() => {
            yojaWebApi.httpClient
                .get({ url: '/hello' })
                .then(res => {
                    section.firstTag('.message')
                           .textContent = res.body;
                });
        });
    }

}

Without the frontend in the JAR — yoja-blueprint-hello-only-server

Same server, same route — but the frontend files live in a local webapp/ folder served from disk via WebApp.folder(path). The yoja-web library is included directly in that folder, not loaded from a JAR. Use this when you want to edit HTML, CSS, or JavaScript and see the change on the next page reload, without rebuilding anything. The first variant (yoja-blueprint-hello) packages everything into the JAR — one artifact, deploy anywhere.

Copy the yoja-web library files into your local folder with:

./gradlew :yoja-web:exportYojaWeb -PfolderDestination=<path/to/your/webapp>

The modules

Pick the modules you need. Each one is documented page-by-page with its full API.

yoja-core

Core

App lifecycle, worker threads, scheduling, HTTP primitives shared by every module.

yoja-http-server

HTTP Server

Declarative routing, sessions, WebSocket, request/response hooks.

yoja-http-client

HTTP Client

Fluent GET/POST builder, JSON-aware bodies, WebSocket dialer.

yoja-reverse-proxy

Reverse Proxy

Host/path-based routing, load balancing, SSL termination, live admin API.

yoja-selenium

Selenium

Embedded server + browser driver + JUnit 6 dynamic tests + JS-module test runners.

yoja-web

Web (frontend)

Section-based components, routing, i18n, HTTP client, WebSocket, storage — in native ES6.

Why choose Yoja

What the framework brings, compared to conventional approaches.

Aspect Conventional approach With Yoja
Frontend tooling Webpack / Vite / npm + transpilation Native ES6 — drop a JS file, reload the page
UI components VDOM framework (React, Vue…) + JSX Standard ES6 classes, HTML directives, direct DOM
Scoped CSS CSS Modules, CSS-in-JS, specificity conflicts yw-css — stylesheet scoped per section via Shadow DOM, zero collision
HTTP server Separate framework (Spring, Quarkus…) + configuration Fluent Java router — sessions, WebSocket in one chain
HTTP client External library + manual Content-Type handling Content-Type auto-detected from the body's runtime type
End-to-end tests Separate tools (Cypress, Playwright) not integrated with the backend Real server + browser + JS modules — from a JUnit 6 @TestFactory
Internationalisation External i18n library + build step yw-language + yw-i18n — XML per section, zero dependency
Reverse proxy Nginx / HAProxy configured separately yoja-reverse-proxy — rules in Java, live admin API
AI compatibility Opaque abstractions, unreadable transpiled output Standard Java + pure ES6 — AI-generated code runs as-is
Dependencies node_modules, bundler, transpiler, source maps Gradle + one jar per module — nothing else