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.
A component-oriented Java web framework: plain Java, plain HTML, no JavaScript build chain.
org.apache.wicket:wicket-core:11.0.0
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.
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.htmlOrderPage.html
<html><body><p wicket:id="message">Message</p></body></html>
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.")); }}
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>
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>
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>
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");
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
Open source since 2004, Wicket 1.0 came out on SourceForge in 2005. The component model it introduced is the one you just used.
wicket-migration take care of the mechanical part of an upgrade.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
Java 21 Jakarta Servlet 6.1 Spring 7
Wicket 10 applications already use jakarta.servlet: the step up is your JDK and container.
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.
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.
An opt-in plain JavaScript Ajax engine with the same client-side API. jQuery stays the default and now supports jQuery 4.
Teams that submitted their application to the Built with Wicket feed.