# Jenkins View Management: How View, ListView, ProxyView, and ViewGroup Work in the Core

> Understand Jenkins view management. Learn how View, ListView, ProxyView, and ViewGroup work together to organize and display your jobs effectively.

- Repository: [Jenkins/jenkins](https://github.com/jenkinsci/jenkins)
- Tags: internals
- Published: 2026-07-28

---

**Jenkins implements view management through an abstract `View` base class that defines the UI contract and persistence layer, with concrete subclasses `ListView` and `ProxyView` handling job display and delegation, while containers such as `Jenkins` and `MyViewsProperty` implement the `ViewGroup` interface to create, own, and organize views.**

Jenkins view management in the `jenkinsci/jenkins` repository is built around a pluggable architecture that lives primarily in the `hudson/model/` package. This design separates the presentation contract from container logic, allowing both global dashboards and personal "My Views" tabs to reuse the same APIs for filtering and displaying jobs.

## The `View` Abstract Class and Core API

The foundation of every view is the abstract class **`View`**, defined in [`hudson/model/View.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/View.java). It specifies the complete contract for a pluggable UI component, including methods for retrieving the view name, description, contained items, and permission checks. `View` also handles configuration persistence via XML ([`config.xml`](https://github.com/jenkinsci/jenkins/blob/main/config.xml)) and provides UI routing through Stapler, making it the base class that all view implementations must extend.

Key responsibilities encoded in `View` include item enumeration, access control through `Permission` checks, and factory-style creation through `View.create(...)`. Because it extends `AbstractModelObject`, it integrates directly with Jenkins' object model and Jelly/Groovy rendering pipeline.

## The `ViewGroup` Interface and View Ownership

Views are not standalone objects; they must belong to a container that implements the **`ViewGroup`** interface ([`hudson/model/ViewGroup.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/ViewGroup.java)). This interface defines the owner contract with methods such as `getViews()`, `add(View)`, `deleteView(View)`, `getView(String)`, and `getPrimaryView()`. Any object that hosts a collection of views—whether the root Jenkins instance or a user's profile—implements `ViewGroup`.

### Global View Storage in the `Jenkins` Singleton

At the top level, the **`Jenkins`** singleton itself implements `ViewGroup`. All global views visible on the main dashboard are stored through `Jenkins.get().getViews()` and manipulated via `Jenkins.addView(View)` or `Jenkins.deleteView(View)`. When an administrator creates a view through the UI, the form submission ultimately delegates to the `Jenkins` instance's `ViewGroup` methods, ensuring the new view is persisted alongside the instance configuration.

### User-Specific "My Views" via `MyViewsProperty`

For personal dashboards, Jenkins attaches a **`MyViewsProperty`** object ([`hudson/model/MyViewsProperty.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/MyViewsProperty.java)) to each `User`. This property implements `ViewGroup`, giving every authenticated user (including anonymous) a private namespace for views accessible under paths like `/user/joe/view/...`. Because it reuses the same `ViewGroup` API, user-specific views support identical creation, deletion, and rendering logic as global views without duplicating code.

## `ListView`: The Default Configurable Job List

The standard concrete view is **`ListView`**, implemented in [`hudson/model/ListView.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/ListView.java). It extends `View` to present jobs in a flat, sortable list with configurable columns and filters. Internally, `ListView` maintains a `SortedSet<String> jobNames` for explicitly included jobs, plus an optional `includeRegex` string for dynamic inclusion.

When `getItems()` is called, the view resolves explicit job names, applies the regular expression filter, and then runs any configured `ViewJobFilter` instances (such as `StatusFilter`). The resulting list is rendered through the view's configured columns, and all settings are persisted in the view's own [`config.xml`](https://github.com/jenkinsci/jenkins/blob/main/config.xml).

## `ProxyView`: Transparent Delegation to Another View

A specialized lightweight implementation is **`ProxyView`** ([`hudson/model/ProxyView.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/ProxyView.java)). Rather than storing its own job list, a `ProxyView` holds only a `proxiedViewName` string and delegates every `View` method—`getItems()`, `contains()`, `getItem()`, and others—to the target view returned by `getProxiedView()`.

This class is useful for creating aliases or shortcuts. It also implements **`StaplerFallback`**, so HTTP requests that do not match a method on `ProxyView` are automatically forwarded to the proxied view, making the alias transparent to end users.

## View Helper Infrastructure

Several supporting classes reduce boilerplate for view management. **`ViewGroupMixIn`** ([`hudson/model/ViewGroupMixIn.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/ViewGroupMixIn.java)) provides helper methods for classes that need view-group capabilities without fully implementing `ViewGroup`. **`ViewDescriptor`** ([`hudson/model/ViewDescriptor.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/ViewDescriptor.java)) serves as the extension point for view types, with each concrete view supplying a descriptor (for example, `ListView.DescriptorImpl`) that drives UI forms and instantiation. Together, these abstractions make Jenkins view management consistent across global, user, and nested scopes.

## Programmatic View Management Example

The following Java snippet demonstrates how to create a `ListView`, add a job, create a `ProxyView` alias, and attach a personal view to a user, using the same APIs that Jenkins calls internally:

```java
// 1. Create a new ListView in the global view group
View newView = View.create(req, rsp, Jenkins.get());   // uses View.create(...)
newView.setDescription("All important jobs");
newView.save();                                       // persists the view

// 2. Add a job to the view programmatically
Job<?,?> job = Jenkins.get().getItem("example-job", Job.class);
((ListView)newView).add(job);                         // adds the job name to the view’s set
newView.save();                                       // persists the change

// 3. Create a ProxyView that points to the ListView created above
ProxyView alias = new ProxyView("alias-view");
alias.setProxiedViewName(newView.getViewName());
Jenkins.get().addView(alias);
alias.save();                                         // now “/view/alias-view/” shows the same jobs as the ListView

// 4. User-specific view group – add a personal view
User me = User.get("joe");
MyViewsProperty myViews = me.getProperty(MyViewsProperty.class);
ListView personal = new ListView("my-jobs");
personal.setDescription("Joe’s personal job list");
myViews.add(personal);
myViews.save();                                       // persists under the user’s config

```

This workflow touches the four core concepts: the `View` factory and persistence, the `ListView` job list, the `ProxyView` delegation, and the `ViewGroup` implementations (`Jenkins` and `MyViewsProperty`).

## Summary

- **`View`** ([`hudson/model/View.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/View.java)) is the abstract base class that defines the contract for every Jenkins view, including item access, permissions, and XML persistence.
- **`ViewGroup`** ([`hudson/model/ViewGroup.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/ViewGroup.java)) is the container interface implemented by `Jenkins` and `MyViewsProperty` to add, delete, rename, and retrieve views.
- **`ListView`** ([`hudson/model/ListView.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/ListView.java)) is the default concrete implementation that displays a configurable, filterable flat list of jobs.
- **`ProxyView`** ([`hudson/model/ProxyView.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/ProxyView.java)) acts as a transparent alias by delegating all operations to another existing view.
- **`MyViewsProperty`** ([`hudson/model/MyViewsProperty.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/MyViewsProperty.java)) brings `ViewGroup` capabilities to individual users, enabling personal "My Views" tabs under `/user/.../view/...`.

## Frequently Asked Questions

### How does Jenkins store and persist views at runtime?

Jenkins persists each view as an XML configuration file, typically [`config.xml`](https://github.com/jenkinsci/jenkins/blob/main/config.xml), within the view's directory under its owning `ViewGroup`. When `save()` is called on a `View` instance, Jenkins serializes the object's fields via XStream. Global views live under the Jenkins home directory, while user-specific views are stored inside the owning `User` object's configuration via `MyViewsProperty`.

### What is the difference between `ListView` and `ProxyView` in Jenkins?

**`ListView`** maintains its own explicit job list and optional regex filter to dynamically resolve items. **`ProxyView`** does not store jobs; it only stores the name of a target view and delegates every method call to that target. `ProxyView` is therefore a lightweight alias, whereas `ListView` is a full-fledged presentation layer.

### Can plugins define custom view types beyond `ListView`?

Yes. Plugins can extend the abstract **`View`** class and provide a corresponding **`ViewDescriptor`** to register a new view type with Jenkins. Because the core architecture treats views as pluggable extensions, custom implementations only need to override item enumeration and UI rendering methods; the `ViewGroup` container handles ownership and persistence automatically.

### How does `ProxyView` handle web requests that do not match its own methods?

`ProxyView` implements **`StaplerFallback`**, a Jenkins interface that tells the Stapler web framework to forward unmatched requests to a fallback object. In `ProxyView`, the fallback is the proxied view itself, so URLs like `/view/alias-view/configure/` are automatically routed to the target view's handler, making the proxy transparent to users and scripts.