Skip to main content
Version: 2026.1

Extending Grid with Custom Columns

Overview

The Studio grid displays element data in configurable columns. Each column is powered by three backend components:

  • Column Definition - declares the column type, its capabilities (sortable, filterable, exportable), and which frontend cell type renders it.
  • Column Resolver - fetches the actual value for a given element and column.
  • Column Collector - lists the available columns that users can add to their grid configuration.

For data objects, columns are generated automatically from class definitions. For custom column types or non-standard sources, you register your own definition, resolver, and collector.

Working example: The studio-example-bundle contains a complete progress bar custom grid column example.


Column Definition

Implement ColumnDefinitionInterface and tag the service with pimcore.studio_backend.grid_column_definition.

interface ColumnDefinitionInterface
{
public function getType(): string;
public function getConfig(mixed $config): array;
public function isSortable(): bool;
public function isFilterable(): bool;
public function getFrontendType(): string;
public function isExportable(): bool;
}

Method Reference

MethodPurpose
getType()Unique type identifier (e.g. 'checkbox', 'data-object.simpleText')
getConfig()Extra config passed to the frontend cell
isSortable()Whether the column supports server-side sorting
isFilterable()Whether the column supports grid filtering
isExportable()Whether the column is included in exports
getFrontendType()Determines which grid cell component renders the column

The Role of getFrontendType()

The getFrontendType() return value tells the frontend which cell renderer to use. If the value matches a built-in frontend type, no frontend work is needed. If you return a custom string, you must register a matching DynamicTypeGridCellAbstract subclass in the Studio UI whose id property equals your getFrontendType() value.

Example

<?php
declare(strict_types=1);

namespace App\Grid\Column\Definition;

use Pimcore\Bundle\StudioBackendBundle\Grid\Column\ColumnDefinitionInterface;

final readonly class ProgressBarDefinition implements
ColumnDefinitionInterface
{
public function getType(): string
{
return 'progressBar';
}

public function getConfig(mixed $config): array
{
return [];
}

public function isSortable(): bool
{
return true;
}

public function isFilterable(): bool
{
return false;
}

public function getFrontendType(): string
{
// Custom type - requires a frontend cell registration.
// Use a built-in FrontendType value to skip frontend work.
return 'progress-bar';
}

public function isExportable(): bool
{
return true;
}
}

Column Resolver

Implement ColumnResolverInterface together with one or both of the element-specific interfaces:

InterfaceResolves FromPriority
CoreElementColumnResolverInterfaceElementInterface (Pimcore core)Higher
StudioElementColumnResolverInterfaceStudioElementInterface (GDI)Lower

If both interfaces are implemented, CoreElementColumnResolverInterface takes precedence.

Tag the service with pimcore.studio_backend.grid_column_resolver.

ColumnData Constructor

The ColumnData value object requires four arguments (plus an optional inheritance parameter):

new ColumnData(
key: string,
locale: ?string,
value: mixed,
fieldType: mixed,
inheritance: null|InheritanceData|array // optional
)

Example

<?php
declare(strict_types=1);

namespace App\Grid\Column\Resolver;

use Pimcore\Bundle\StudioBackendBundle\Grid\Column\ColumnResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\Grid\Column\CoreElementColumnResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\Grid\Column\StudioElementColumnResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\Grid\Schema\Column;
use Pimcore\Bundle\StudioBackendBundle\Grid\Schema\ColumnData;
use Pimcore\Bundle\StudioBackendBundle\Response\StudioElementInterface;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
use Pimcore\Model\Element\ElementInterface;

final class ProgressBarResolver implements
ColumnResolverInterface,
CoreElementColumnResolverInterface,
StudioElementColumnResolverInterface
{
public function resolveForCoreElement(
Column $column,
ElementInterface $element
): ColumnData {
return new ColumnData(
key: $column->getKey(),
locale: $column->getLocale(),
value: $element->getProperty('progress') ?? 0,
fieldType: 'progressBar'
);
}

public function resolveForStudioElement(
Column $column,
StudioElementInterface $element
): ColumnData {
return new ColumnData(
key: $column->getKey(),
locale: $column->getLocale(),
value: 0,
fieldType: 'progressBar'
);
}

public function getType(): string
{
return 'progressBar';
}

public function supportedElementTypes(): array
{
return [
ElementTypes::TYPE_ASSET,
ElementTypes::TYPE_DOCUMENT,
ElementTypes::TYPE_OBJECT,
];
}
}

Column Collector

Implement ColumnCollectorInterface and tag the service with pimcore.studio_backend.grid_column_collector.

The collector receives all registered column definitions keyed by type. It returns ColumnConfiguration objects describing the columns available to users.

ColumnConfiguration Constructor

All 11 parameters are required:

new ColumnConfiguration(
key: string,
group: array,
sortable: bool,
editable: bool,
exportable: bool,
filterable: bool,
localizable: bool,
locale: ?string,
type: string,
frontendType: string,
config: array,
)

Example

<?php
declare(strict_types=1);

namespace App\Grid\Column\Collector;

use Pimcore\Bundle\StudioBackendBundle\Grid\Column\ColumnCollectorInterface;
use Pimcore\Bundle\StudioBackendBundle\Grid\Column\ColumnDefinitionInterface;
use Pimcore\Bundle\StudioBackendBundle\Grid\Schema\ColumnConfiguration;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;

final readonly class ProgressBarCollector implements
ColumnCollectorInterface
{
public function getCollectorName(): string
{
return 'progress-bar';
}

/**
* @param ColumnDefinitionInterface[] $availableColumnDefinitions
*
* @return ColumnConfiguration[]
*/
public function getColumnConfigurations(
array $availableColumnDefinitions
): array {
$definition = $availableColumnDefinitions['progressBar'];

return [
new ColumnConfiguration(
key: 'progress',
group: ['progress'],
sortable: $definition->isSortable(),
editable: false,
exportable: $definition->isExportable(),
filterable: $definition->isFilterable(),
localizable: false,
locale: null,
type: 'progressBar',
frontendType: $definition->getFrontendType(),
config: $definition->getConfig(null)
),
];
}

public function supportedElementTypes(): array
{
return [
ElementTypes::TYPE_ASSET,
];
}
}

Service Registration

Register all three components with their respective DI tags:

ComponentTag
Column Definitionpimcore.studio_backend.grid_column_definition
Column Resolverpimcore.studio_backend.grid_column_resolver
Column Collectorpimcore.studio_backend.grid_column_collector
services:
App\Grid\Column\Definition\ProgressBarDefinition:
tags:
- { name: pimcore.studio_backend.grid_column_definition }

App\Grid\Column\Resolver\ProgressBarResolver:
tags:
- { name: pimcore.studio_backend.grid_column_resolver }

App\Grid\Column\Collector\ProgressBarCollector:
tags:
- { name: pimcore.studio_backend.grid_column_collector }

Once registered, the column appears in the available columns API (e.g. /pimcore-studio/api/assets/grid/available-configuration).

To skip a custom data object field type from automatic column generation, add it to the skip list:

pimcore_studio_backend:
grid:
data_object:
skip_field_types:
- "myCustomFieldType"

For more information on grid architecture, see the Grid documentation.


Built-in Frontend Types

The FrontendType enum provides the following built-in values. When getFrontendType() returns one of these, the Studio UI renders the column without any frontend plugin work:

Enum CaseString Value
ELEMENT_DROPZONEelement_dropzone
INPUTinput
IDid
TEXTAREAtextarea
SELECTselect
MULTISELECTmultiselect
CHECKBOXcheckbox
DATETIMEdatetime
IMAGEimage
ASSET_LINKasset-link
OBJECT_LINKobject-link
ASSET_PREVIEWasset-preview
BOOLEANboolean

If you return a custom string not in this list, you must register a matching frontend cell type. See the Custom Grid Columns cross-layer guide for details.


Predefined Columns for Grids

You can define what columns should be visible by default in the grid by modifying the pimcore_studio_backend.grid.asset.predefined_columns parameter in your config.yaml file.

Example:

pimcore_studio_backend:
grid:
asset:
predefined_columns:
- key: id
group: system
- key: fullpath
group: system
data_object:
predefined_columns:
- key: id
group: system
- key: fullpath
group: system

Predefined Columns for Search Grids

You can define what columns should be visible by default in the search grid by modifying the pimcore_studio_backend.search_grid.asset.predefined_columns parameter in your config.yaml file.

Example:

pimcore_studio_backend:
search_grid:
asset:
predefined_columns:
- key: id
group: system
- key: fullpath
group: system

Transformers

You can define transformers for Advanced Columns. Transformers modify column data before it is displayed in the grid. Implement TransformerInterface and tag with pimcore.studio_backend.grid_transformer.

The transform method receives and returns an array of AdvancedValue objects. If transformation fails, throw a Pimcore\Bundle\StudioBackendBundle\Exception\Api\TransformerException.

<?php
declare(strict_types=1);

namespace App\Transformer;

use Pimcore\Bundle\StudioBackendBundle\Grid\Column\TransformerInterface;

final class Uppercase implements TransformerInterface
{
/**
* @param AdvancedValue[] $value
* @return AdvancedValue[]
*/
public function transform(array $value, array $config): array
{
foreach ($value as $val) {
$val->setValue(strtoupper($val->getValue()));
}

return $value;
}

public function getName(): string
{
return 'Uppercase';
}

public function getKey(): string
{
return 'uppercase';
}

public function getDescription(): string
{
return 'Transforms the value to uppercase.';
}

public function getConfigOptions(): array
{
return [];
}
}

Cross-Layer Guide

For custom frontend types that require frontend registration, see the Custom Grid Columns cross-layer guide.

Example Bundle

See the studio-example-bundle for a complete working example including backend definition, resolver, collector, and frontend cell type.