# How Jenkins Handles View Rendering and UI Decorators: Architecture and Extension Points

> Discover how Jenkins handles view rendering and UI decorators using its layered model-view architecture and PageDecorators for plugin integration. Learn about extension points.

- Repository: [Jenkins/jenkins](https://github.com/jenkinsci/jenkins)
- Tags: architecture
- Published: 2026-06-19

---

**Jenkins employs a layered model-view architecture where `View` objects supply data, Stapler routes requests to Jelly templates, and the `PageDecorator` hierarchy enables plugins to inject global HTML, CSS, or HTTP headers into every page.**

Jenkins (jenkinsci/jenkins) separates data models from presentation through a robust view rendering pipeline. Understanding how Jenkins handles view rendering and UI decorators is essential for developers building custom dashboard plugins or modifying the global UI appearance without touching core code.

## The View Rendering Pipeline

The rendering process follows a strict pipeline from model to browser, implemented across several core classes in the `hudson.model` package.

### Model Definition with View.java

At the core of every dashboard lies `hudson.model.View`, an abstract class representing logical collections of jobs or resources. Concrete implementations like `ListView` extend this base to provide specialized data structures. The class implements `AccessControlled`, ensuring **security checks** execute before any rendering begins.

Path: [`core/src/main/java/hudson/model/View.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/View.java)

### Metadata and Factory Pattern via ViewDescriptor

Each view type pairs with a `hudson.model.ViewDescriptor` that supplies metadata including display names, icons, and configuration forms. The descriptor acts as a factory through its `newInstance` method, allowing Jenkins to instantiate concrete views dynamically when users create new dashboards.

Path: [`core/src/main/java/hudson/model/ViewDescriptor.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/ViewDescriptor.java)

### URL Routing with ViewGroup and ViewGroupMixIn

Stapler exposes views at URLs like `/view/<name>/` through `hudson.model.ViewGroup` and its companion `ViewGroupMixIn`. These classes manage the view namespace and cache view instances, materializing them only when specific URLs are requested to support **lazy loading**.

Path: [`core/src/main/java/hudson/model/ViewGroup.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/ViewGroup.java)

### Jelly Template Rendering

The actual HTML generation occurs in `core/src/main/resources/lib/layout/view.jelly`. This template imports the global layout, inserts view-specific fragments from the concrete view's `index.jelly`, and stitches the final response. The view template serves as the primary layout container for all view pages.

Path: `core/src/main/resources/lib/layout/view.jelly`

### Customizing Navigation with ViewsTabBar

The left-hand tab bar is modularized through `hudson.views.ViewsTabBar`, an extension point that plugins can implement to reorder tabs or inject custom navigation elements. The default implementation renders standard view tabs, but custom implementations can return arbitrary HTML fragments to modify the navigation interface.

Path: [`core/src/main/java/hudson/views/ViewsTabBar.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/views/ViewsTabBar.java)

## UI Decorators and Global Page Modifications

Jenkins enables site-wide visual modifications through the `PageDecorator` extension point, allowing plugins to modify every page without altering core templates.

### The PageDecorator Extension Point

`hudson.model.PageDecorator` extends `Descriptor<PageDecorator>` and serves as the base class for global UI injections. Implementations register automatically via the `@Extension` annotation and can inject content into the HTML `<head>` or `<body>`, or modify HTTP headers. The `decorate(StaplerRequest, StaplerResponse, Object)` method receives the request and response objects, permitting modifications before final transmission.

Path: [`core/src/main/java/hudson/model/PageDecorator.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/PageDecorator.java)

### Simplified Static Decorators

For static resources like favicons or simple header injections, `jenkins.model.SimplePageDecorator` provides a convenience subclass. These decorators bind to URLs and are accessible via `Jenkins#getDescriptor`, streamlining the creation of resource-only modifications.

Path: [`core/src/main/java/jenkins/model/SimplePageDecorator.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/jenkins/model/SimplePageDecorator.java)

### Security-Focused Decorator Examples

Several built-in decorators demonstrate security applications:

- **CspDecorator**: Located at [`core/src/main/java/jenkins/security/csp/impl/CspDecorator.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/jenkins/security/csp/impl/CspDecorator.java), emits `Content-Security-Policy` HTTP headers on every response.
- **FrameOptionsPageDecorator**: Located at [`core/src/main/java/jenkins/security/FrameOptionsPageDecorator.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/jenkins/security/FrameOptionsPageDecorator.java), adds `X-Frame-Options` headers to prevent click-jacking attacks.

Both extend `PageDecorator` and can be enabled or disabled via system properties.

### Decorator Aggregation and Application

During request processing, `hudson.Functions.getPageDecorators()` iterates over the static `PageDecorator.ALL` list (a `DescriptorList` populated at startup). Each decorator's `decorate` method executes in sequence, allowing cumulative modifications to the response.

Path: [`core/src/main/java/hudson/Functions.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/Functions.java)

## Interaction Between Views and Decorators

The view-specific Jelly template renders after global decorators have injected their fragments, ensuring consistent page framing. Decorators can inspect the current view model via `request.getAttribute("view")` to customize output per-view. Security checks in `View` execute before decorator code runs, preventing unauthorized users from receiving decorated pages for restricted views.

## Implementation Examples

### Creating a Custom View Type

Extend `ListView` and provide a descriptor:

```java
package com.example;

import hudson.model.ListView;
import hudson.model.ViewDescriptor;
import hudson.Extension;

public class MyListView extends ListView {
    @Extension
    public static class DescriptorImpl extends ViewDescriptor {
        @Override
        public String getDisplayName() {
            return "My List View";
        }
    }
}

```

This registers the view in Jenkins' "New View" dialog, automatically utilizing `view.jelly` for rendering.

### Implementing a Global Banner Decorator

Inject HTML into every page:

```java
package com.example;

import hudson.model.PageDecorator;
import hudson.Extension;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

@Extension
public class BannerDecorator extends PageDecorator {

    @Override
    public void decorate(StaplerRequest req, StaplerResponse rsp, Object bean) {
        rsp.addHeader("X-Banner", "<div class='myBanner'>Jenkins is awesome!</div>");
    }
}

```

The `@Extension` annotation ensures automatic discovery and registration in `PageDecorator.ALL`.

### Customizing the Views Tab Bar

Replace the default tab navigation:

```java
package com.example;

import hudson.views.ViewsTabBar;
import hudson.Extension;
import hudson.model.User;

@Extension
public class CustomTabBar extends ViewsTabBar {
    @Override
    public String getTabs(User user) {
        return "<a href='custom'>Custom Tab</a>";
    }
}

```

This overrides the left-hand tab bar without modifying core view rendering logic.

## Summary

- **Jenkins view rendering** separates data (`View`), metadata (`ViewDescriptor`), and presentation (Jelly templates) through a pipeline managed by Stapler.
- **URL routing** flows through `ViewGroup` and `ViewGroupMixIn`, supporting lazy loading of view instances.
- **UI decorators** use the `PageDecorator` extension point to inject global HTML, CSS, or HTTP headers, aggregated via `Functions.getPageDecorators()`.
- **Security** checks execute in `View` before decorators run, ensuring access control precedes UI modification.
- **Extension points** (`View`, `ViewsTabBar`, `PageDecorator`) allow plugin developers to customize dashboards and global UI without core code changes.

## Frequently Asked Questions

### What is the difference between View and ViewDescriptor in Jenkins?

`View` represents the runtime data model containing jobs and configuration, while `ViewDescriptor` provides the metadata and factory methods required to create and configure view instances. The descriptor supplies the display name and icon shown in the Jenkins UI, whereas the view object manages the actual collection of items and their rendering state.

### How do PageDecorators affect all Jenkins pages?

`PageDecorator` implementations are discovered at startup via the `@Extension` annotation and stored in the static `PageDecorator.ALL` list. For every HTTP request, `Functions.getPageDecorators()` iterates this list and invokes each decorator's `decorate()` method, allowing injection of HTML fragments or HTTP headers into every response regardless of the specific view being rendered.

### Can I disable specific UI decorators in Jenkins?

Yes. Many decorators check system properties or configuration flags before applying modifications. For example, `FrameOptionsPageDecorator` can be disabled via system properties, and custom implementations can implement conditional logic within their `decorate()` methods to skip processing based on request attributes or global configuration settings.

### How does Stapler know which Jelly template to render for a view?

Stapler uses convention-based routing where the view object (returned from `ViewGroup`) serves as the model. The framework looks for `index.jelly` in the view's resource path, while `core/src/main/resources/lib/layout/view.jelly` provides the outer layout. The view-specific template is included within the global layout, creating the complete page structure.