Apache Wicket , version 11

A component-oriented Java web framework: plain Java, plain HTML, no JavaScript build chain.

org.apache.wicket:wicket-core:11.0.0

A page is a tree of components

Wicket is component oriented and renders on the server. Build one small order page in seven steps: each adds a few lines of plain Java or HTML, and one branch to the page’s component tree.

  1. 01 Start with plain HTML

    A page begins as an HTML file. The wicket:id attribute marks the tag a component will render. There is no template language: the file stays HTML that any editor or browser opens.

    Tree after step 1

    • OrderPageWebPageOrderPage.html

    OrderPage.html

    <html><body><p wicket:id="message">Message</p></body></html>
  2. 02 Give the tag a component

    Behaviour lives in a Java class with the same name. add(...) puts a Label into the page’s tree under the id "message", and Wicket renders it into the matching tag.

    Tree after step 2

    • OrderPageWebPageOrderPage.html
      • "message"Label<p>

    OrderPage.java

    public class OrderPage extends WebPage {    public OrderPage() {        add(new Label("message",            "Check both addresses."));    }}
  3. 03 Add a form with fields

    Components nest the way tags do. The form is a child of the page; the text fields and the button are children of the form. A CompoundPropertyModel binds each field to the property with its id.

    Tree after step 3

    • OrderPageWebPageOrderPage.html
      • "message"Label<p>
      • "order"Form<form>
        • "street"TextField<input>
        • "city"TextField<input>
        • "save"Button<button>

    OrderPage.java constructor

    Form<Address> form = new Form<>("order",    new CompoundPropertyModel<>(billingAddress));form.add(new TextField<String>("street"));form.add(new TextField<String>("city"));form.add(new Button("save"));add(form);

    OrderPage.html body

    <p wicket:id="message">Message</p><form wicket:id="order">  <input wicket:id="street">  <input wicket:id="city">  <button wicket:id="save">Save</button></form>
  4. 04 Reuse a panel, twice

    Move the fields into AddressPanel: a Java class with its own markup inside <wicket:panel> and its own model. The form needs no model any more and uses the panel twice, for billing and for shipping: the same branch grows under each.

    Tree after step 4

    • OrderPageWebPageOrderPage.html
      • "message"Label<p>
      • "order"Form<form>
        • "billing"AddressPanel<div>
          • "street"TextField<input>
          • "city"TextField<input>
        • "shipping"AddressPanel<div>
          • "street"TextField<input>
          • "city"TextField<input>
        • "save"Button<button>

    AddressPanel.java

    public class AddressPanel extends Panel {    public AddressPanel(String id,            IModel<Address> address) {        super(id,            new CompoundPropertyModel<>(address));        add(new TextField<String>("street"));        add(new TextField<String>("city"));    }}

    AddressPanel.html

    <wicket:panel>  <input wicket:id="street">  <input wicket:id="city"></wicket:panel>

    OrderPage.java constructor

    Form<Void> form = new Form<>("order");form.add(new AddressPanel("billing", billingAddress));form.add(new AddressPanel("shipping", shippingAddress));

    OrderPage.html form

    <div wicket:id="billing"></div><div wicket:id="shipping"></div>
  5. 05 Update it from Java

    Wicket renders on the server and keeps the page’s state there, in this tree. A click on the AjaxLink runs onClick in Java; target.add(shipping) re-renders only that panel and Wicket swaps it into the page. You write no JavaScript.

    Tree after step 5

    • OrderPageWebPageOrderPage.html
      • "message"Label<p>
      • "order"Form<form>
        • "billing"AddressPanel<div>
          • "street"TextField<input>
          • "city"TextField<input>
        • "shipElsewhere"AjaxLink<a>
        • "shipping"AddressPanel<div>
          • "street"TextField<input>
          • "city"TextField<input>
        • "save"Button<button>

    OrderPage.java constructor

    AddressPanel shipping =    new AddressPanel("shipping", shippingAddress);shipping.setOutputMarkupPlaceholderTag(true);shipping.setVisible(false);form.add(shipping); form.add(new AjaxLink<Void>("shipElsewhere") {    @Override    public void onClick(AjaxRequestTarget target) {        shipping.setVisible(!shipping.isVisible());        target.add(shipping);    }});

    OrderPage.html form

    <div wicket:id="billing"></div><a wicket:id="shipElsewhere">Ship to another address</a><div wicket:id="shipping"></div>
  6. 06 Test it without a browser

    WicketTester renders the page in a plain unit test: no browser, no container. It finds components by their path in the tree, clicks the Ajax link and checks what came back.

    Tree after step 6

    • OrderPageWebPageOrderPage.html
      • "message"Label<p>
      • "order"Form<form>
        • "billing"AddressPanel<div>
          • "street"TextField<input>
          • "city"TextField<input>
        • "shipElsewhere"AjaxLink<a>
        • "shipping"AddressPanel<div>
          • "street"TextField<input>
          • "city"TextField<input>
        • "save"Button<button>

    OrderPageTest.java

    WicketTester tester =    new WicketTester(new MyApplication());tester.startPage(OrderPage.class);tester.assertLabel("message", "Check both addresses.");tester.assertInvisible("order:shipping"); tester.clickLink("order:shipElsewhere");tester.assertVisible("order:shipping");tester.assertComponentOnAjaxResponse("order:shipping");
  7. 07 Secure by default

    Wicket supports a strict Content Security Policy without unsafe-inline and adds a nonce to every header contribution, the Ajax link’s script included. You only add what your application needs.

    Tree after step 7

    • OrderPageWebPageOrderPage.html
      • "message"Label<p>
      • "order"Form<form>
        • "billing"AddressPanel<div>
          • "street"TextField<input>
          • "city"TextField<input>
        • "shipElsewhere"AjaxLink<a>
        • "shipping"AddressPanel<div>
          • "street"TextField<input>
          • "city"TextField<input>
        • "save"Button<button>

    MyApplication.java

    @Overrideprotected void init() {    super.init();    // CSP is on by default; allow one extra image host    getCspSettings().blocking()        .add(CSPDirective.IMG_SRC,             "https://images.example.org");}

That is the whole model, in a project of your own in a minute: Open the quick start

Stable since 2005

Open source since 2004, Wicket 1.0 came out on SourceForge in 2005. The component model it introduced is the one you just used.

An Apache project
Developed in the open at the Apache Software Foundation by a community of volunteers, under the Apache License 2.0.
A stable API
Breaking changes only when needed, deprecated alternatives kept where possible, and every change documented in the migration guide.
Long-term support
Every fourth major release is an LTS, supported until the next LTS. At most two release lines are maintained at once.
Migration recipes
OpenRewrite recipes in wicket-migration take care of the mechanical part of an upgrade.

New in Wicket 11

Wicket 11 opens the quarterly release schedule. It moves to a current Java and Jakarta baseline, tightens security, makes every component leaner and lets Ajax run without jQuery. Everything about Wicket 11

A new baseline

Java 21 Jakarta Servlet 6.1 Spring 7

Wicket 10 applications already use jakarta.servlet: the step up is your JDK and container.

More secure by default

Encrypted URLs and stored pages cannot be altered unnoticed, model values in markup are escaped consistently, and CSP covers script-src-attr and style-src-attr.

Leaner components

One compact state object per component: in the benchmarks, reading state is 30 to 45% faster and a tree with Ajax behaviors serializes about 40% smaller.

Ajax without jQuery

An opt-in plain JavaScript Ajax engine with the same client-side API. jQuery stays the default and now supports jQuery 4.

Built with Wicket

Teams that submitted their application to the Built with Wicket feed.