First published in 2011 as a slide deck. Rewritten in September 2026 for version 6.1.1 of the plugin and Struts 7. The original slides are still on SlideShare; they show the plugin as it was then, while the syntax above is the current one.
Introduction to the Struts2 jQuery Grid Tags
The Struts2 jQuery Grid Plugin wraps jqGrid in two JSP tags. sjg:grid renders the table, sjg:gridColumn describes a column, and a Struts action delivers the rows as JSON. Paging and sorting run on the server, so the browser only ever holds one page of data.
This walkthrough uses version 6.1.1 of the plugin, which targets Struts 7 and Java 17.
Dependencies
<dependency>
<groupId>com.jgeppert.struts2.jquery</groupId>
<artifactId>struts2-jquery-grid-plugin</artifactId>
<version>6.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>7.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>7.3.0</version>
</dependency>Code language: HTML, XML (xml)
The grid plugin brings the core jQuery plugin and Struts itself as transitive dependencies. The JSON plugin provides the result type the action needs, and the convention plugin lets the action configure itself through annotations.
The page
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sj" uri="/struts-jquery-tags" %>
<%@ taglib prefix="sjg" uri="/struts-jquery-grid-tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Customers</title>
<sj:head jqueryui="true"/>
</head>
<body>
<s:url var="customersUrl" action="customers-json"/>
<sjg:grid id="customers"
caption="Customers"
dataType="json"
href="%{customersUrl}"
gridModel="gridModel"
pager="true"
rowNum="15"
rowList="15,30,50"
viewrecords="true"
sortname="name"
sortorder="asc"
autowidth="true">
<sjg:gridColumn name="id" index="id" title="ID" key="true" width="60"
formatter="integer" sortable="true"/>
<sjg:gridColumn name="name" index="name" title="Company" sortable="true"/>
<sjg:gridColumn name="city" index="city" title="City" sortable="true"/>
<sjg:gridColumn name="country" index="country" title="Country" sortable="true"/>
<sjg:gridColumn name="creditLimit" index="creditLimit" title="Credit limit"
align="right" formatter="currency" sortable="true"/>
</sjg:grid>
</body>
</html>Code language: HTML, XML (xml)
sj:head loads jQuery and jQuery UI. href points at the action that delivers the data, and gridModel names the property in its JSON response that holds the rows.
In each column, name is the property of the row object and index is what the grid sends back as the sort field when someone clicks the header. Keep both the same unless you have a reason not to. rowNum sets the page size, rowList the choices offered in the pager, and sortname with sortorder the initial order.
The model
The JSON result serializes objects through their getters, so a plain class with getters is all a row needs:
package com.example.model;
public class Customer {
private final int id;
private final String name;
private final String city;
private final String country;
private final double creditLimit;
public Customer(int id, String name, String city, String country, double creditLimit) {
this.id = id;
this.name = name;
this.city = city;
this.country = country;
this.creditLimit = creditLimit;
}
public int getId() { return id; }
public String getName() { return name; }
public String getCity() { return city; }
public String getCountry() { return country; }
public double getCreditLimit() { return creditLimit; }
}Code language: Java (java)
The action
The grid sends four request parameters: rows for the page size, page for the requested page, sidx for the sort column and sord for the direction. It expects the rows back under the name given in gridModel, together with page, total for the number of pages, and records for the number of rows.
package com.example.actions;
import com.example.model.Customer;
import org.apache.struts2.ActionSupport;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import java.util.Comparator;
import java.util.List;
@ParentPackage("json-default")
@Result(type = "json")
public class CustomersJsonAction extends ActionSupport {
// In a real application this comes from a repository or DAO.
private static final List<Customer> CUSTOMERS = List.of(
new Customer(1, "Nordwind Handel", "Hamburg", "Germany", 21000),
new Customer(2, "Lakeside Tools", "Chicago", "USA", 118000),
new Customer(3, "Riviere Import", "Lyon", "France", 64500),
new Customer(4, "Alpine Supply", "Innsbruck", "Austria", 30000));
// Request: sent by the grid
private int rows = 15;
private int page = 1;
private String sidx;
private String sord = "asc";
// Response: serialized by the JSON result
private List<Customer> gridModel;
private int total;
private int records;
@Override
public String execute() {
int size = Math.max(rows, 1);
int current = Math.max(page, 1);
records = CUSTOMERS.size();
total = (int) Math.ceil((double) records / size);
Comparator<Customer> order = comparatorFor(sidx);
if ("desc".equals(sord)) {
order = order.reversed();
}
gridModel = CUSTOMERS.stream()
.sorted(order)
.skip((long) (current - 1) * size)
.limit(size)
.toList();
return SUCCESS;
}
// Only known columns are accepted; anything else falls back to the id.
private static Comparator<Customer> comparatorFor(String column) {
return switch (column == null ? "" : column) {
case "name" -> Comparator.comparing(Customer::getName);
case "city" -> Comparator.comparing(Customer::getCity);
case "country" -> Comparator.comparing(Customer::getCountry);
case "creditLimit" -> Comparator.comparingDouble(Customer::getCreditLimit);
default -> Comparator.comparingInt(Customer::getId);
};
}
@StrutsParameter
public void setRows(int rows) { this.rows = rows; }
@StrutsParameter
public void setPage(int page) { this.page = page; }
@StrutsParameter
public void setSidx(String sidx) { this.sidx = sidx; }
@StrutsParameter
public void setSord(String sord) { this.sord = sord; }
public List<Customer> getGridModel() { return gridModel; }
public int getPage() { return page; }
public int getTotal() { return total; }
public int getRecords() { return records; }
}Code language: Java (java)
The convention plugin maps CustomersJsonAction to customers-json, the name the JSP uses in s:url. @ParentPackage("json-default") makes the JSON result type available to the action.
The example sorts and pages a list in memory to stay self-contained. With a database, the same four values turn into ORDER BY and LIMIT/OFFSET in the query, and records comes from a COUNT.
If paging and sorting do nothing
The grid loads the first page, but clicking a header or the next-page button changes nothing. On Struts 7 the cause is almost always a missing @StrutsParameter.
Since Struts 7, struts.parameters.requireAnnotations is on by default. A request parameter only reaches a setter that carries the annotation; the others are ignored. The grid sends page and sidx correctly, the action never sees them, and so it keeps answering with page one in the default order. Tutorials written for older Struts versions don’t mention this, because it wasn’t needed.
Don’t trust sidx
sidx comes straight from the browser. The switch above only accepts known column names and falls back to the ID for anything else. Keep that whitelist when the data comes from SQL: a sort field concatenated into an ORDER BY clause is an injection point, and it’s one that code reviews tend to miss because it doesn’t look like user input.
Going further
The grid does a lot more than this: inline editing through editurl, a search toolbar, subgrids, and grouping. The grid showcase has a working example for each, and the source code is on GitHub.
First published in 2011. Rewritten in September 2026 for version 6.1.1 of the plugin and Struts 7.
love it,it’s great
Hello..
I have a problem in retrieving the date column of an sjg column from the corresponding action class.The date is properly displayed on the page which is picked up from json action class when the grid page is loaded. I am trying to get the date in a java.util.Date field of action class and to save the record but all other fields that is strings integers are working but when trying to add/edit a record’s date field not entering into the execute function of the action class and it is going to action input type. Is the format required is in a particular format? I tried a sample with sj tags date picker it was working fine.My only doubt is, Is there anything to do with the json object over here.
It would be great if you could help me by any ways.
Thanks in advance..
Raji
It is Most easy way for solve this problem
I want know how to validate the input from the addRow button?
I am trying to implement a select box in search dialog for a grid column. I found this:- http://code.google.com/p/struts2-jquery/wiki/SearchGrid .
now i am doing exactly same thing but still not getting any list in search dialog.
My jsp looks like this:
Can you please ask this question in the User Group?
http://groups.google.com/group/struts2-jquery
Please add some Code examples.