Getting Started
A complete Hello World in three files. JWeb is a Spring Boot library — add one dependency and write pages in pure Java. No frontend toolchain, no templates, no build step.
1. Add the Dependency
Maven — the JitPack repository plus the dependency:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.oscar-osmig</groupId>
<artifactId>Jweb</artifactId>
<version>v3.0.3</version>
</dependency>v3.0.3 ▾
Gradle:
repositories { maven { url 'https://jitpack.io' } }
dependencies { implementation 'com.github.oscar-osmig:Jweb:v3.0.3' }2. Project Structure
Three files. Java allows one public class per file, so each of these is its own file — putting them in one file will not compile:
src/main/java/org/example/
App.java <- starts the application
Routes.java <- maps URLs to pages
pages/
HomePage.java <- the page itself3. App.java
@JWebApplication replaces @SpringBootApplication. Framework beans arrive through auto-configuration, so only your own package is scanned.
package org.example;
import jweb.JWebApplication;
import org.springframework.boot.SpringApplication;
@JWebApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}4. Routes.java
A @Component implementing JWebRoutes. Page routes render a Template; router routes take the request and can return anything.
package org.example;
import jweb.JWeb;
import jweb.JWebRoutes;
import jweb.Middlewares;
import jweb.api.Component;
// HomePage lives in org.example.pages — see step 5
class HomePage implements Template { public Element render() { return div("Home"); } }
@Component
public class Routes implements JWebRoutes {
@Override
public void configure(JWeb app) {
app.use(Middlewares.recommended()); // security headers, request ids
app.pages("/", HomePage.class);
app.get("/hello", req -> "Hello from JWeb");
}
}5. pages/HomePage.java
A page is any class implementing Template. Elements come from El, styles from the CSS DSL — both fully type-checked.
package org.example.pages;
import jweb.Element;
import jweb.Template;
import static jweb.El.*;
import static jweb.Css.*;
public class HomePage implements Template {
@Override
public Element render() {
return div(style().maxWidth(px(700)).margin(zero, auto).padding(rem(3)),
h1(style().fontSize(rem(2)).fontWeight(700), "Welcome"),
p(style().color(hex("#64748b")), "Your first JWeb page.")
);
}
}6. Run It
mvn spring-boot:run
# / -> your page
# /hello -> Hello from JWebNext: a Shared Layout (optional)
Once you have more than one page, a layout gives them a common shell. It is a Template that takes the page content:
class HomePage implements Template { public Element render() { return div("Home"); } }
class AboutPage implements Template { public Element render() { return div("About"); } }
public class MainLayout implements Template {
private final Element content;
public MainLayout(Element content) { this.content = content; }
@Override
public Element render() {
return html(
head(metaCharset(), metaViewport(), title("My App")),
body(nav(a(href("/"), "Home")), main(content))
);
}
}
// in Routes.configure:
app.layout(MainLayout.class)
.pages("/", HomePage.class,
"/about", AboutPage.class);Configuration (optional)
Everything has a working default — set only what you need:
server:
port: 8080
jweb:
dev:
debug: false # stack traces on error pages (dev only)
data: # MongoDB, off by default
enabled: false
ai: # built-in AI, off by default
enabled: false
api-key: ${AI_API_KEY:}Skip the Setup
The CLI generates this whole structure, wired and ready to run:
jweb new myapp --package=com.mycompany.myapp
cd myapp && mvn spring-boot:run