JSTL Core Tags
JSTL Core Tags
One of the cardinal sins of early JSP development was embedding Java code directly inside view files using scriptlets. The result was templates that were impossible to hand to a designer, painful to test, and fragile to maintain. The JSP Standard Tag Library (JSTL) was introduced specifically to solve this problem by replacing scriptlets with a vocabulary of XML-like tags. The Core tag library — prefix c: — covers the everyday need: conditionals, iteration, and scoped variables. Master these four tags and your JSPs become readable, testable, and design-friendly.
Adding JSTL to Your Project
JSTL is not part of the Jakarta EE servlet spec itself; you need to add the implementation JAR. With Maven:
Then declare the Core taglib at the top of every JSP that uses it:
jakarta.tags.core URI. On older Tomcat 9 / Java EE projects, the URI is http://java.sun.com/jsp/jstl/core. Always match the JSTL version to your servlet container version.
c:set — Storing Values in Scope
<c:set> is the JSTL replacement for a scriptlet variable declaration. It stores a value in one of the four JSP scopes: page, request, session, or application. The default scope is page.
A particularly useful pattern is using c:set to promote a request attribute into session scope after an action, or to create a local alias for a deeply nested property so you do not repeat the full EL path throughout the page.
c:set as an alias: If you reference ${order.customer.billingAddress.city} five times on a page, set it once — <c:set var="city" value="${order.customer.billingAddress.city}"/> — then use ${city}. Shorter, faster (one EL evaluation), and easier to rename.
c:if — Simple Conditionals
<c:if> renders its body only when the test attribute evaluates to true. It is the straightforward one-branch conditional.
Notice the EL operators: empty checks for null or an empty collection/string, not empty is its inverse, gt means greater-than, and eq means equals. Using these textual operators avoids the need to escape < and > inside XML/HTML.
c:if has no else. If you need an if/else construct, you need c:choose. Using two opposing c:if tests is fragile — if the condition changes, you must update both tags and risk introducing a gap or an overlap.
c:choose — Multi-Branch Conditionals
<c:choose> with nested <c:when> and <c:otherwise> is JSTL's equivalent of a switch/if-else chain. Only the first matching when block is rendered; otherwise is the fallback if nothing matches.
The c:otherwise block serves a double purpose: it renders a sensible default AND it acts as a safety net when new enum values are added to the backend without a matching c:when. Always include it.
c:forEach — Iteration
<c:forEach> is JSTL's iterator. It works over any java.lang.Iterable, arrays, Map entries, and comma-separated strings. It is the single most frequently used JSTL tag in a typical web application.
Basic list rendering:
The varStatus attribute exposes a loop-status object with useful properties:
varStatus properties at a glance:
index— zero-based index of the current iterationcount— one-based count (index + 1), great for row numbersfirst— boolean, true on the first iterationlast— boolean, true on the final iteration
Ranging over integers (no collection needed):
Iterating a Map:
Combining Tags: A Complete Example
Real JSPs combine all four tags. Here is a realistic product listing page fragment showing a Servlet placing attributes into the request, and the JSP rendering them without a single scriptlet:
${products.size() gt 0} works, but ${not empty products} is idiomatic JSTL and handles null gracefully — size() on a null reference throws a NullPointerException.
Why These Tags Belong in Every JSP
These four Core tags implement the logic-free view principle: business decisions (what data to show, how to compute a total) belong in the Servlet or service layer; the JSP's only job is to render a given state. c:if/c:choose express display decisions such as "show an empty-state message" or "add a CSS class when this is the first item". c:forEach renders collections prepared by the controller. c:set creates local aliases to keep the template readable. Together they let a designer edit the HTML without touching any Java, and let a developer unit-test all logic before the view is even written.
Summary
c:set— declare a scoped variable or alias; eliminates scriptlet<% %>declarations.c:if— single-branch conditional; use when you only need a positive guard.c:choose/c:when/c:otherwise— multi-branch conditional; the clean alternative to cascadingc:ifpairs.c:forEach— iterate any collection, array, map, or integer range; usevarStatusfor index, count, first/last flags.
In the next lesson you will extend JSTL with the Formatting and Functions tag libraries to handle dates, numbers, and string manipulation — keeping formatting logic out of your Java code as well.