Jakarta Pages custom tags package reusable view behavior behind a tag name. Simple tag handlers extend SimpleTagSupport and implement doTag; tag files offer a JSP-based alternative. Use tags for presentation concerns such as formatting or controlled layout, not database access or domain decisions.
A custom tag lets a team express repeated UI patterns as readable tags: alerts, buttons, cards, permission blocks, or status badges.
Tag files are good for markup-heavy fragments. SimpleTag handlers are Java classes for behavior-heavy tags.
Custom tags should render or format. They should not query the database, open connections, or enforce business workflows.
Declare attributes with clear required and runtime-expression rules in a TLD or tag file. Escape untrusted values for their output context and decide whether the tag evaluates its body once, conditionally, or not at all. A tag handler may be reused by the container, so do not retain request-specific mutable state beyond one invocation.
Prefer JSTL and EL for standard iteration, conditions, and formatting. Build a custom tag only when it gives several views one stable, testable presentation contract. Keep generated markup accessible and avoid a tag that hides large page-level navigation or business workflows.
A tag file under WEB-INF/tags is compiled by the container and is useful when the reusable behavior is mostly markup. Attribute directives define the public contract; jsp:doBody renders caller-supplied body content.
<%-- /WEB-INF/tags/alert.tag --%>\n<%@ tag body-content="scriptless" %>\n<%@ attribute name="type" required="true" %>\n<div class="alert alert-${type}" role="status">\n <jsp:doBody />\n</div>\n\n<%-- page.jsp --%>\n<%@ taglib prefix="ui" tagdir="/WEB-INF/tags" %>\n<ui:alert type="success">Profile saved.</ui:alert>
The tag owns the wrapper while the page supplies the message body. A production tag library should map an approved finite set of variants rather than accepting an arbitrary class fragment.
For a Java handler, extend SimpleTagSupport, expose bean-style setters for declared attributes, and write output through getJspContext().getOut() inside doTag(). Invoke getJspBody().invoke(writer) only when the body is permitted and should run.
Containers may pool or reuse handler instances. Reset per-invocation state and never store request data in static fields. A handler that writes user text must escape for HTML, attribute, URL, or JavaScript context as appropriate.
<app:actionButton permission="USER_DELETE" label="Delete" url="/users/delete" />
Try this next
0 of 2 completed
No. They belong to the view layer and should render data prepared elsewhere.
Explore 500+ free tutorials across 20+ languages and frameworks.