Groovy Server Pages are the engine behind the view layer in the Grails framework. If you’ve worked with JSP before, GSP will feel familiar, but faster to write, easier to maintain, and far more expressive thanks to the Groovy language beneath it.
Here I will explain everything you need to know: from GSP syntax and built-in tags to Sitemesh layouts, XSS prevention, and how Groovy server pages compare to modern alternatives. Whether you’re starting a new Grails project or evaluating GSP for server-side rendering, this post gives you the exact details to get started and go deep.
Key Takeaways
- Groovy server pages (GSP) serve as the view layer in the Grails framework, allowing for dynamic web content using HTML and Groovy code.
- GSP files, with a .gsp extension, reside in the grails-app/views directory and benefit from Java’s performance on the JVM.
- Key syntax elements include expressions, scriptlets, comments, and tags starting with ‘g:’.
- GSP supports features like reusable templates, layouts with Sitemesh, and form handling for user input.
- It’s well-suited for rapid MVP development, particularly for teams with a Java background, offering server-side rendering by default.
Table of Contents
What Are Groovy Server Pages?
Groovy Server Pages (GSP) is the primary view rendering technology in the Grails framework. A GSP file is a mix of HTML markup and Groovy code, processed server-side to generate dynamic web content.
Key facts:
- GSP files use the
.gspextension and live in thegrails-app/viewsdirectory - As of Grails 3.3, GSP is an independent plugin, not bundled in the core framework
- It runs on the Java Virtual Machine (JVM), inheriting Java’s security and performance characteristics
- It is designed to be familiar to ASP and JSP developers, but more flexible and intuitive
To add GSP to a Grails project, declare the dependency in build.gradle:
dependencies {
implementation "org.grails.plugins:gsp:6.2.3"
}
For production compilation, apply the Gradle plugin:
apply plugin: "org.grails.grails-gsp"

How GSP Fits the Grails MVC Architecture
Grails follows the MVC Design Pattern (Model-View-Controller), and Groovy server pages serve as the View layer.
Here’s how the flow works:
- A browser sends a request to a Grails controller
- The controller queries the database via GORM (Grails Object Relational Mapping) and builds a model, a map of key-value data
- That model is passed to a GSP view
- The GSP renders the model into HTML and sends it back to the browser
Example controller action:
def show() {
[book: Book.get(params.id)]
}
Corresponding GSP expression:
${book.title}
The Grails framework uses Convention over Configuration: if a controller action is named list()Grails automatically looks for grails-app/views/[controller]/list.gsp, no manual wiring required.
GSP Syntax: The Core Elements
Understanding Groovy server page elements starts with four building blocks.
1. Expressions ${expr}
The most common form of GSP is Groovy server pages code. Any Groovy expression goes inside ${}:
<p>Welcome, ${user.name}!</p>
<p>Today is: ${new Date()}</p>
Expressions are automatically HTML-encoded by default (more on that in the security section).
2. Scriptlets <% %> and <%= %>
Scriptlets (<% %>) embed raw Groovy code blocks. Output scriptlets (<%= %>) print a value directly:
<% out << "Hello from a scriptlet" %>
<%= "Output this string" %>
Important: The official Groovy server pages documentation strongly discourages scriptlets. Use GSP tags or custom tag libraries instead to maintain clean separation of concerns.
3. Server-Side Comments <%-- --%>
Groovy server pages comments use JSP-style syntax and are never sent to the client:
<%-- This comment is stripped at compile time --%>
Unlike HTML comments (<!-- --> These are invisible in the browser's source.
4. Page Directives
Groovy server pages import statements and content-type declarations use the directive syntax:
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page contentType="application/json" %>
Groovy imports most standard classes automatically, so explicit imports are rarely needed.
Essential Built-in GSP Tags
All GSP tags in Grails use the g: prefix and require no import declarations.
Here are the ones you’ll use most.
Conditional Logic <g:if> and <g:else>
<g:if test="${session.role == 'admin'}">
<a href="/admin">Admin Panel</a>
</g:if>
<g:else>
<p>Access restricted.</p>
</g:else>
Iterative Tags <g:each>
The <g:each> tag iterates over any Groovy collection:
<g:each in="${bookList}" var="book">
<li>${book.title} by ${book.author}</li>
</g:each>
Link Generation <g:link>
The <g:link> tag builds URLs based on controller and action names, not hardcoded paths:
<g:link controller="book" action="show" id="${book.id}">
View Book
</g:link>
Pagination <g:paginate>
The GSP pagination tag generates full next/previous navigation with a breadcrumb trail:
<g:paginate controller="book" action="list" total="${bookCount}" />
Key attributes for <g:paginate> (per the official Grails 7.0.0 documentation):
| Attribute | Required | Default | Description |
|---|---|---|---|
total | ✅ Yes | — | Total number of results |
max | No | 10 | Records per page |
maxsteps | No | 10 | Number of pagination step links |
prev | No | “Previous” | Label for previous link |
next | No | “Next” | Label for next link |
omitFirst | No | false | Hide the first page link when not in range |
omitLast | No | false | Hide the last page link when not in range |

Working with Forms and User Input
Groovy server pages form handling is clean and controller-aware.
Here’s a full example with a Groovy server pages text field:
<g:form controller="employee" action="save">
First Name: <g:textField name="fName" value="${emp.fName}" />
Last Name: <g:textField name="lName" value="${emp.lName}" />
Active: <g:checkBox name="active" value="${emp.active}" />
Department: <g:select name="dept" value="${emp.dept}"
from="['Engineering', 'Sales', 'HR']" />
<g:submitButton name="submit" value="Save" />
</g:form>
GSP also provides <g:passwordField>, <g:hiddenField>, <g:radio>, and <g:actionSubmit> for forms with multiple submit buttons.
Handling Scopes and Parameters
GSP has direct access to five variable scopes:
| Scope | Usage |
|---|---|
params | URL and form request parameters |
request | Per-request attributes |
session | Per-user session data |
flash | Short-lived data (survives one redirect) |
application | Application-wide shared state |
Use <g:set> to define variables within a GSP:
<g:set var="now" value="${new Date()}" scope="request" />
Advanced View Techniques
Templates for Reusability
GSP templates and layouts start with reusable partial views. By convention, template filenames begin with an underscore:
grails-app/views/book/_bookCard.gsp
Render a template with:
<g:render template="bookCard" model="[book: myBook]" />
To render a collection in one call:
<g:render template="bookCard" var="book" collection="${bookList}" />
Sitemesh Layouts and Decorators
Grails Sitemesh layouts live in grails-app/views/layouts/. A layout wraps your individual views with shared structure, headers, footers, and navigation:
<html>
<head>
<title><g:layoutTitle default="My App" /></title>
<g:layoutHead />
</head>
<body>
<div class="nav"><!-- shared nav --></div>
<div class="content">
<g:layoutBody />
</div>
</body>
</html>
Three ways to trigger a Sitemesh layout:
- Meta tag in the view,
<meta name="layout" content="main" /> - Controller-level declaration,
static layout = 'customer' - Convention, Grails looks for
grails-app/views/layouts/BookController.gspautomatically
The default application layout is grails-app/views/layouts/application.gsp. Override it in application.yml:
grails:
sitemesh:
default.layout: myLayoutName
Use <g:applyLayout> for an inline layout application to sections or templates:
<g:applyLayout name="myLayout" template="bookTemplate" collection="${books}" />
Custom Tag Libraries (TagLib)
Custom Tag Libraries in GSP let you encapsulate complex view logic into clean, reusable tags. Create a Groovy class in grails-app/taglib/ with a name ending in TagLib:
class FormatTagLib {
def sayHi = { attrs ->
out << "Hello, ${attrs.name}!"
}
}
Use it in any GSP without imports:
<g:sayHi name="${user.name}" />
Performance and Security
GSP Compilation to Java Servlets
Groovy server pages compile into Java classes at runtime via the GroovyPagesTemplateEngine and GroovyPagesServlet. This means GSP benefits from JVM-level performance once compiled, not interpreted on each request.
For production deployments, pre-compile all GSPs at build time using the grails-gsp Gradle plugin. This eliminates first-request compilation overhead entirely.
GSP reload configuration (development only):
grails.gsp.enable.reload = true
grails.gsp.reload.interval = 5000 // ms between last-modified checks
grails.gsp.reload.granularity = 1000 // ms leeway for last-modified comparison
Note: Frequent GSP reloading in long-running development sessions can cause PermGen (or Metaspace) exhaustion—restart the server if memory issues arise.
XSS Prevention (Cross-Site Scripting)
GSP’s security defaults are strict. As of Grails 2.3, all ${}expressions are HTML-encoded by default. The default codec configuration application.yml is:
grails:
views:
gsp:
encoding: UTF-8
htmlcodec: xml
codecs:
expression: html # escapes values inside ${}
scriptlets: html # escapes scriptlet output
taglib: none # tag authors control their own encoding
staticparts: none # raw markup is not re-encoded
To output trusted HTML content without encoding, use raw():
<section>${raw(page.content)}</section>
Use this only for content you control. Never call raw() on user-supplied data.
Additional built-in protections:
- GORM automatically escapes SQL to prevent SQL injection
<g:link>,<g:form>, and<g:createLink>apply URL encoding automatically- Custom codecs like
encodeAsHTML(),encodeAsURL(), andencodeAsJavaScript()are available for edge cases

GSP vs. JSP vs. Modern Frontend Frameworks
| Feature | GSP (Grails) | JSP (Spring MVC) | React / Angular |
|---|---|---|---|
| Language | Groovy | Java / EL | JavaScript / TypeScript |
| Templating Style | Tags + expressions | Tags + scriptlets | Component-based |
| Server-Side Rendering (SSR) | ✅ Native | ✅ Native | ⚠️ Requires configuration |
| HTML Auto-Escaping | ✅ Default (since 2.3) | ❌ Manual | ✅ Default |
| Layout Engine | Sitemesh | Tiles / Thymeleaf | CSS frameworks |
| Convention over Config | ✅ Strong | ❌ Weak | ❌ N/A |
| GORM Integration | ✅ Built-in | ❌ Requires setup | ❌ N/A |
| Learning Curve (Java devs) | Low | Low | Medium–High |
| Best For | Rapid MVPs, full-stack Grails | Spring apps | SPAs, complex UI |
Running Groovy Server Pages Without Grails
Running Groovy server pages without Grails is technically possible using groovy.servlet.TemplateServlet with a standard servlet container. However, the tag library ecosystem, Sitemesh integration, and GORM connectivity are all Grails-specific. Outside of Grails, GSP loses most of what makes it productive.
For Groovy server pages Spring MVC integration (non-Grails), the GSP plugin can be wired into a Spring Boot project, but the setup requires manual configuration of the GroovyPagesTemplateEngine bean. Official Groovy server pages documentation for this use case is limited.
Why GSP Still Makes Sense in 2026
Groovy server pages remain a practical choice for specific scenarios:
- Rapid MVP development: Grails scaffolding generates
create.gsp,edit.gsp,index.gsp, andshow.gspautomatically withgrails generate-views - Full-stack JVM projects: Tight integration between GORM, controllers, and views reduces boilerplate
- Teams with Java backgrounds: The learning curve is shallow compared to React or Angular
- Server-side rendering by default: No client-side hydration, no JavaScript bundle size concerns
As noted in the Grails community, GSP is one of the best ways to build and demonstrate a product quickly in early-stage startups, especially when backend and frontend need to move in lockstep.
FAQs
Both are server-side templating technologies. GSP uses Groovy expressions and g: tag libraries with automatic HTML encoding by default. JSP uses Java EL and custom tag libraries but lacks Grails’ convention-based view resolution and tight GORM integration. For a detailed breakdown, see the comparison table above.
GSP files live in the grails-app/views/ directory. Templates (reusable partials) use an underscore prefix (e.g., _bookCard.gsp). Layout files go in grails-app/views/layouts/.
Yes. The official GSP plugin documentation for the 7.0.0-SNAPSHOT is at https://grails.apache.org/docs-legacy-gsp/snapshot/guide/. The main Grails 7.0.0 security and web layer docs are at https://grails.apache.org/docs/7.0.0/.
By default, Grails HTML-encodes all ${} expressions since version 2.3. Avoid using raw() user-supplied input. Configure the filteringCodecForContentType option application.yml for maximum output encoding at the response level.
Yes. GSP can serve the initial HTML shell that loads a JavaScript frontend. The two are independent layers. You can build REST APIs in Grails controllers and consume them from Angular or React while still using GSP for pages that benefit from server-side rendering.











