Strongly-typed HTML/CSS/JS component model for Java — the heart of JWebMP.
The core defines the HTML language in Java — every HTML element, CSS property, event, and feature is a first-class Java object using CRTP generics. It generates the HTML, JavaScript, and CSS required for any page, and provides the SPI contracts that allow a mass ecosystem of plugins to be created that adhere to the component model.
Every component serves dual purposes:
toString(true) renders the component as HTML (with all queued CSS/JS) — use this for server-side rendering, template generation, or serving pages on the Vert.x routertoString() renders the component as JSON — use this for AJAX responses, API payloads, or any scenario where the component tree needs to be serializedPages can optionally be annotated with @PageConfiguration and served directly via the Vert.x HTTP server, or components can be used standalone to generate HTML/JSON without a server at all.
Provides Page, the full HTML element library (Div, Span, Table, Form, …), a typed CSS builder, 50+ server-driven event adapters, page configurators that inject scripts/styles before render, and a Jackson-serialized AJAX pipeline for live DOM updates. Extension is SPI-driven via ServiceLoader.
Built on JWebMP Client · GuicedEE · Vert.x 5 · JPMS module com.jwebmp.core · Java 25+
<dependency>
<groupId>com.jwebmp.core</groupId>
<artifactId>core</artifactId>
</dependency>
Version is managed by the JWebMP BOM.
toString(true) produces full HTML with all CSS/JS assets; toString() produces JSON for AJAX/API consumption — same component tree, two output formatsDiv, Span, Table, Form, Input, Select, Canvas, Video, Article, Section, …) is a typed Java class with CRTP fluent API; child constraints are enforced at compile timeInputTextType, InputEmailType, InputNumberType, InputDateType, InputFileType, InputCheckBoxType, InputRadioType, and 15 more — each with the correct HTML attributes@CSS) across 14 sub-packages: backgrounds, borders, colours, fonts, margins, padding, displays, lists, tables, text, outline, measurement, height/width, image — rendered inline or composed via CSSPropertiesFactoryOnClickAdapter, OnChangeAdapter, OnSubmitAdapter, OnDragAdapter, OnKeyDownAdapter, OnMouseEnterAdapter, and many more — each backed by a ServiceLoader SPI (IOnClickService, IOnChangeService, etc.). Events must be their own named classes — anonymous inner classes and lambdas are not supported because the framework relies on class identity for serialization and ServiceLoader discoveryIPageConfigurator SPI is an optional hook that modifies pages before rendering — use it to inject CSS links, JavaScript references, dynamic scripts, or top-shelf scripts in priority order; pages work perfectly fine without any configuratorsRenderBeforeLinks, RenderAfterLinks, RenderBeforeScripts, RenderAfterScripts, RenderBeforeDynamicScripts, RenderAfterDynamicScripts — fine-grained control over asset insertionAjaxCall/AjaxResponse (from jwebmp-client) carry event payloads and DOM update instructions; Event base class wires server handlers to browser eventsFeature<O, J> wraps a JavaScript library with typed options (JavaScriptPart), CSS/JS references, and automatic dependency ordering. Features must be their own named classes — the framework uses class identity for deduplication, dependency ordering, and Jackson serializationContentSecurityPolicy builder for CSP headersJWebMPJacksonModule registers custom event deserializers at pre-startupComponentBase → ComponentHierarchyBase → ComponentHTMLBase → ComponentHTMLAttributeBase → ComponentHTMLOptionsBase → ComponentStyleBase → ComponentThemeBase → ComponentDataBindingBase → ComponentDependencyBase → ComponentFeatureBase → ComponentEventBase → Component → your subclassGUIDGenerator, ColourUtils, TextUtilities, EscapeChars, DateUtils, regex patterns (email, date, text)No server needed — just create components and render:
Page<?> page = new Page<>();
Div<?, ?, ?> container = new Div<>();
container.add(new Paragraph<>().setText("Welcome to JWebMP"));
page.getBody().add(container);
String html = page.toString(true); // full HTML + queued CSS/JS
String json = page.toString(); // JSON representation of the component tree
Annotate with @PageConfiguration to host the page on the Vert.x HTTP server — this is optional and only needed when you want server-side rendering via Vert.x:
@PageConfiguration(url = "/")
public class HomePage extends Page<HomePage> {
public HomePage() {
getBody().add(new H1<>().setText("Hello from JWebMP"));
getBody().add(new Paragraph<>().setText("Server-rendered, type-safe HTML."));
}
}
module my.app {
requires com.jwebmp.core;
provides com.jwebmp.core.services.IPage
with my.app.HomePage;
}
Boot GuicedEE — the page is now live:
IGuiceContext.instance().inject();
// Vert.x HTTP server starts, HomePage served at "/"
Events must be their own named classes — define a top-level or static inner class:
public class ButtonClickEvent extends OnClickAdapter {
public ButtonClickEvent(Component component) {
super(component);
}
@Override
public void onClick(AjaxCall<?> call, AjaxResponse<?> response) {
response.addComponent(new Paragraph<>().setText("Clicked!"));
}
}
Then attach it to any component:
Div<?, ?, ?> button = new Div<>();
button.setText("Click me");
button.addEvent(new ButtonClickEvent(button));
page.getBody().add(button);
⚠️ Anonymous inner classes and lambdas are not supported for events or features. The framework relies on class identity for serialization,
ServiceLoaderdiscovery, and deduplication. Always create a named class.
public class TooltipFeature extends Feature<TooltipOptions, TooltipFeature> {
public TooltipFeature(IComponentHierarchyBase<?, ?> component) {
super("tooltip", component);
getOptions().setPlacement("top");
}
@Override
protected void assignFunctionalityToComponent() {
addQuery(getComponent().asBase().getJQueryID() + "tooltip(" + getOptions() + ");");
}
}
Div<?, ?, ?> styled = new Div<>();
styled.getCss()
.getBackground().setBackgroundColor$(ColourNames.AliceBlue);
styled.getCss()
.getBorder().setBorderWidth(new MeasurementCSSImpl(1, MeasurementTypes.Pixels));
styled.getCss()
.getFont().setFontSize(new MeasurementCSSImpl(14, MeasurementTypes.Pixels));
IGuiceContext.instance().inject()
└─ JWebMPPreStartup (IGuicePreStartup, sortOrder=15)
└─ Register JWebMPJacksonModule with ObjectMapper
HTTP request arrives at @PageConfiguration URL
└─ IPage instance created (via Guice binding → Page.class)
└─ IPageConfigurator chain (sorted by sortOrder)
├─ CSSLinksInsertPageConfigurator
│ ├─ RenderBeforeLinks SPIs
│ ├─ CSS <link> tags inserted into <head>
│ └─ RenderAfterLinks SPIs
├─ ScriptsInsertPageConfigurator
│ ├─ RenderBeforeScripts SPIs
│ ├─ <script> tags inserted (by RequirementsPriority)
│ └─ RenderAfterScripts SPIs
├─ TopShelfScriptsInsertPageConfigurator
│ └─ High-priority scripts in <head>
└─ ScriptsDynamicPageConfigurator
├─ RenderBeforeDynamicScripts SPIs
├─ Inline/dynamic <script> blocks
└─ RenderAfterDynamicScripts SPIs
└─ page.toString(true) → full HTML response
All SPIs are discovered via ServiceLoader. Register implementations with JPMS provides...with or META-INF/services.
IPageProvide a page implementation — typically by subclassing Page:
@PageConfiguration(url = "/dashboard")
public class Dashboard extends Page<Dashboard> {
public Dashboard() {
getBody().add(new H1<>().setText("Dashboard"));
}
@Override
public AjaxResponse<?> onConnect(AjaxCall<?> call, AjaxResponse<?> response) {
// handle initial connection
return response;
}
}
IPageConfiguratorConfigure pages before rendering — inject CSS, JS, meta tags, or transform the component tree:
public class AnalyticsConfigurator
implements IPageConfigurator<AnalyticsConfigurator> {
@Override
public IPage<?> configure(IPage<?> page) {
page.addJavaScriptReference(new JavascriptReference("analytics", 1.0, "analytics.js"));
return page;
}
@Override
public Integer sortOrder() {
return 500; // after core configurators
}
}
IEventConfiguratorTransform any Event after creation — add global behaviour to all events:
public class EventLogger implements IEventConfigurator<EventLogger> {
@Override
public Event<?, ?> configureEvent(Event<?, ?> event) {
// add logging, metrics, or security checks
return event;
}
}
| SPI | Purpose |
|---|---|
RenderBeforeLinks |
Insert content before CSS <link> tags |
RenderAfterLinks |
Insert content after CSS <link> tags |
RenderBeforeScripts |
Insert content before <script> tags |
RenderAfterScripts |
Insert content after <script> tags |
RenderBeforeDynamicScripts |
Insert content before dynamic/inline scripts |
RenderAfterDynamicScripts |
Insert content after dynamic/inline scripts |
Each of the 50+ event types has a corresponding service interface discovered via ServiceLoader:
| SPI | Event |
|---|---|
IOnClickService |
Click |
IOnChangeService |
Change |
IOnSubmitService |
Submit |
IOnKeyDownService |
Key down |
IOnMouseEnterService |
Mouse enter |
IOnDragService / IOnDropService |
Drag & drop |
IOnFocusService / IOnBlurService |
Focus / blur |
| … and 40+ more | See com.jwebmp.core.events.services |
The CRTP component chain provides progressive enhancement — each layer adds a capability:
ComponentBase → ID, name, properties, JSON serialization
└─ ComponentHierarchyBase → parent/child tree, add(), CSS/JS references
└─ ComponentHTMLBase → tag rendering, text, raw HTML, newlines
└─ ComponentHTMLAttributeBase → HTML attributes (typed enums)
└─ ComponentHTMLOptionsBase → JavaScript options (JavaScriptPart)
└─ ComponentStyleBase → inline CSS via the CSS builder
└─ ComponentThemeBase → theme support
└─ ComponentDataBindingBase → data-bind hooks
└─ ComponentDependencyBase → CSS/JS dependency refs
└─ ComponentFeatureBase → Feature attachment
└─ ComponentEventBase → Event attachment
└─ Component → final user-facing base
Concrete HTML elements (Div, Table, Form, etc.) extend Component and lock down child/attribute/event types via generics.
Note: The hierarchy depth is a design-time concern only. Modern JDK versions flatten class hierarchies at runtime, so the 11-layer chain has zero performance overhead — you get full type safety with no cost.
| Package | Properties |
|---|---|
css.backgrounds |
background-color, background-image, background-repeat, … |
css.borders |
border-width, border-style, border-color, border-radius, … |
css.colours |
Named colours, colour enumeration |
css.fonts |
font-family, font-size, font-weight, font-style, … |
css.margins |
margin-top, margin-right, margin-bottom, margin-left |
css.padding |
padding-top, padding-right, padding-bottom, padding-left |
css.displays |
display, visibility, overflow, position, float, … |
css.text |
text-align, text-decoration, text-transform, line-height, … |
css.tables |
border-collapse, border-spacing, table-layout, … |
css.lists |
list-style-type, list-style-position, … |
css.outline |
outline-width, outline-style, outline-color |
css.heightwidth |
height, width, min-height, max-width, … |
css.measurement |
MeasurementCSSImpl — px, em, rem, %, vw, vh, … |
css.animatable |
Animatable property markers |
| Variable / Property | Default | Purpose |
|---|---|---|
@PageConfiguration(url=) |
"/" |
URL path for page discovery |
| Environment-driven ports | via jwebmp-vertx | HTTP/HTTPS listen ports |
com.jwebmp.core
├── com.jwebmp.client (SPI contracts, AjaxCall/Response, interceptors)
├── com.guicedee.client (DI, lifecycle, CallScope)
├── com.guicedee.vertx (Vert.x lifecycle, event bus)
├── com.guicedee.jsonrepresentation (Jackson ObjectMapper, IJsonRepresentation)
├── io.vertx.core (Vert.x core)
├── io.vertx.web (Vert.x Web — Router)
├── com.google.guice (Dependency injection)
├── com.fasterxml.jackson.databind (JSON serialization)
├── jakarta.validation (Bean Validation)
├── org.apache.commons.lang3 (String utilities)
├── org.apache.commons.text (Text utilities)
├── org.apache.commons.io (IO utilities)
├── net.sf.uadetector.core (User-agent parsing)
└── net.sf.uadetector.resources
Module name: com.jwebmp.core
The module:
com.jwebmp.core, com.jwebmp.core.base, com.jwebmp.core.base.html, com.jwebmp.core.base.html.inputs, com.jwebmp.core.base.page, com.jwebmp.core.components, com.jwebmp.core.events.* (50+ packages), com.jwebmp.core.htmlbuilder.css.* (14 packages), com.jwebmp.core.utilities, com.jwebmp.core.enumerations, com.jwebmp.core.implementationsIGuiceModule with JWebMPServicesBindings, IGuicePreStartup with JWebMPPreStartup, IGuiceScanModuleInclusions with JWebMPModuleInclusions, IPageConfigurator with CSSLinksInsertPageConfigurator, ScriptsInsertPageConfigurator, ScriptsDynamicPageConfigurator, TopShelfScriptsInsertPageConfigurator, IRegularExpressions with regex pattern providersIPage, IPageConfigurator, IErrorPage, IEventConfigurator, IDynamicRenderingServlet, IOnComponentConfigured, IOnDataBind, IOnDataBindCloak, IOnComponentHtmlRender, IOnComponentAdded, IAfterRenderComplete, all RenderBefore*/RenderAfter* SPIs, all 50+ event service SPIs| Class | Role |
|---|---|
Page |
Top-level HTML page — subclass and annotate with @PageConfiguration |
Component |
Base class for all HTML elements — CRTP with typed children, attributes, features, events |
CSSComponent |
CSS-only component (renders as class, no HTML tag) |
Feature |
Wraps a JS library — typed options, CSS/JS references, jQuery integration |
Event |
Server-driven event handler — wires browser events to AjaxCall/AjaxResponse |
DataAdapter |
Bridges server data into raw JSON for component consumption |
ContentSecurityPolicy |
Builder for CSP headers |
PageOptions |
Page-level options (title, author, favicon, keywords, etc.) |
CSSPropertiesFactory |
Renders @CSS-annotated objects into inline CSS strings |
JWebMPServicesBindings |
IGuiceModule — binds IPage, page configurators, render-ordering SPIs |
JWebMPPreStartup |
IGuicePreStartup — registers JWebMPJacksonModule at startup |
JWebMPJacksonModule |
Jackson module — custom Event and IEventTypes deserializers |
IEventConfigurator |
SPI — transform any Event after creation |
GUIDGenerator |
Unique ID generation for components |
| Element | Class | Children type |
|---|---|---|
<div> |
Div |
GlobalChildren |
<span> |
Span |
GlobalChildren |
<p> |
Paragraph |
GlobalChildren |
<h1>–<h6> |
H1–H6 |
GlobalChildren |
<table> |
Table |
TableHeaderGroup, TableBodyGroup, TableFooterGroup |
<form> |
Form |
FormChildren |
<input> |
Input + 22 typed variants |
inline |
<select> |
Select |
Option, OptionGroup |
<ul> / <ol> |
List |
ListItem |
<a> |
Link |
GlobalChildren |
<img> |
Image |
inline |
<button> |
Button |
GlobalChildren |
<header> / <footer> / <nav> / <section> / <article> / <aside> |
Semantic elements | GlobalChildren |
<canvas> / <video> / <audio> |
Media elements | Source, Track |
<script> / <style> / <link> |
Asset elements | — |
# Build
mvn -B -ntp -DskipTests package
# Test
mvn -B -ntp verify
Prerequisites: Java 25+, Maven 3.9+
Issues and pull requests are welcome at github.com/JWebMP/Core/issues.