Build web applications from Java components and plain HTML

Apache Wicket is a component-oriented Java web framework, open source since 2004 and a project of the Apache Software Foundation since 2007. Every component keeps its behaviour and state in Java and its markup in plain HTML, so there is no JavaScript build chain to maintain.

Get started

Releases

The Apache Wicket release lines in service, until when each is supported, and the lines that have reached end of life
In service
Wicket 10 LTS 10.12.0 supported until Jul 2027
Wicket 11 Current 11.0.0 supported until Jan 2027
End of life
Wicket 9 9.25.0 ended Oct 2026
Wicket 8 8.20.0 ended Oct 2026

Upgrade pathsAll releases and downloads

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");}

See the examples runningComponents and markup

Why Wicket

Coming from Spring MVC, Jakarta Faces or Vaadin? Why component-oriented compares Wicket with request-based and other component frameworks, what it gains and what it costs, and the glossary gives each Wicket term with what other frameworks call it.

Also included: translations in over 25 languages, safe use of multiple tabs and windows, injection with CDI, Spring and Guice, Jakarta EE integration, and component-local JavaScript and CSS.

Built with Wicket

Many applications run on Wicket without saying so. These say so, collected through the Built with Wicket feed. Submit your own project.

Learn Wicket in five lessons

The lessons build one application, from an empty project to a tested page with Ajax. Every page of the documentation says which Wicket versions it covers.

  1. 01Build your first application
  2. 02Give every page the same layout
  3. 03Build a form that saves data
  4. 04Update a page with Ajax
  5. 05Test a page with WicketTester

Start here, in the documentation

Built by the people who use it

Apache Wicket is developed in the open by volunteers. Answer a question on the users list, report a bug with a quickstart, improve the documentation, or send a pull request to apache/wicket: a committer reviews it, and it goes out with the next release of its line.

How to contributeCommunity and support