Core
JWebMP is a Java Web UI Framework for the rapid development of data bound web applications. It allows single language, bi-directional databinding and many more features
Install / Use
/learn @JWebMP/CoreREADME
JWebMP Core
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 serialized
Pages 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+
📦 Installation
<dependency>
<groupId>com.jwebmp.core</groupId>
<artifactId>core</artifactId>
</dependency>
<details>
<summary>Gradle (Kotlin DSL)</summary>
implementation("com.jwebmp.core:jwebmp-core:2.0.0-SNAPSHOT")
</details>
Version is managed by the JWebMP BOM.
✨ Features
- HTML language in Java — the core defines the entire HTML5 element set as typed Java classes, along with CSS and JavaScript rendering rules, forming the foundation that all JWebMP plugins build on top of
- Dual rendering modes —
toString(true)produces full HTML with all CSS/JS assets;toString()produces JSON for AJAX/API consumption — same component tree, two output formats - Complete HTML element library — every HTML5 element (
Div,Span,Table,Form,Input,Select,Canvas,Video,Article,Section, …) is a typed Java class with CRTP fluent API; child constraints are enforced at compile time - Typed input elements —
InputTextType,InputEmailType,InputNumberType,InputDateType,InputFileType,InputCheckBoxType,InputRadioType, and 15 more — each with the correct HTML attributes - Typed CSS builder — annotation-driven CSS properties (
@CSS) across 14 sub-packages: backgrounds, borders, colours, fonts, margins, padding, displays, lists, tables, text, outline, measurement, height/width, image — rendered inline or composed viaCSSPropertiesFactory - 50+ server-driven event adapters —
OnClickAdapter,OnChangeAdapter,OnSubmitAdapter,OnDragAdapter,OnKeyDownAdapter,OnMouseEnterAdapter, and many more — each backed by aServiceLoaderSPI (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 andServiceLoaderdiscovery - Page configurators (optional) —
IPageConfiguratorSPI 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 configurators - Render-ordering hooks —
RenderBeforeLinks,RenderAfterLinks,RenderBeforeScripts,RenderAfterScripts,RenderBeforeDynamicScripts,RenderAfterDynamicScripts— fine-grained control over asset insertion - AJAX pipeline —
AjaxCall/AjaxResponse(from jwebmp-client) carry event payloads and DOM update instructions;Eventbase class wires server handlers to browser events - Feature system —
Feature<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 serialization - CSSComponent — render-only CSS classes (no HTML tag) for reusable style-only components
- DataAdapter — bridges server-side data into raw JSON for component consumption
- Content Security Policy —
ContentSecurityPolicybuilder for CSP headers - Jackson module —
JWebMPJacksonModuleregisters custom event deserializers at pre-startup - Component hierarchy — 11-layer deep CRTP chain:
ComponentBase→ComponentHierarchyBase→ComponentHTMLBase→ComponentHTMLAttributeBase→ComponentHTMLOptionsBase→ComponentStyleBase→ComponentThemeBase→ComponentDataBindingBase→ComponentDependencyBase→ComponentFeatureBase→ComponentEventBase→Component→ your subclass - Utility belt —
GUIDGenerator,ColourUtils,TextUtilities,EscapeChars,DateUtils, regex patterns (email, date, text)
🚀 Quick Start
Build a page in pure Java
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
Optionally serve it with GuicedEE + Vert.x
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 "/"
Handle a click event
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.
Add a Feature with typed options
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() + ");");
}
}
Use the CSS builder
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));
📐 Page Render Flow
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) → ful
