Back to Twenty

Views

packages/twenty-docs/developers/extend/apps/layout/views.mdx

2.8.07.4 KB
Original Source

A view is a saved configuration for how records of an object are displayed: which fields appear, their order, whether they're visible, and any filters or groups applied. Use defineView() to ship pre-configured views with your app — typically a default index view for each custom object you create.

ts
import { defineView, ViewKey } from 'twenty-sdk/define';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object';
import { NAME_FIELD_UNIVERSAL_IDENTIFIER } from '../objects/example-object';

export default defineView({
  universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
  name: 'All example items',
  objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
  icon: 'IconList',
  key: ViewKey.INDEX,
  position: 0,
  fields: [
    {
      universalIdentifier: 'f926bdb7-6af7-4683-9a09-adbca56c29f0',
      fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
      position: 0,
      isVisible: true,
      size: 200,
    },
  ],
});

Key points

  • objectUniversalIdentifier specifies which object this view applies to. It can be a custom object you defined or a standard Twenty object.
  • key determines the view type — ViewKey.INDEX is the main list view for the object.
  • fields controls which columns appear and in what order. Each field references a fieldMetadataUniversalIdentifier.
  • You can also declare filters, filterGroups, groups, and fieldGroups for advanced configurations.
  • position controls ordering when multiple views exist for the same object.

Filters

A view can ship with pre-applied filters. Each filter has three coordinates: the field being filtered, the operand (how to compare), and the value (what to compare against). All three must line up — using an operand that doesn't apply to a field type will be rejected at sync time.

ts
import { ViewFilterOperand } from 'twenty-shared/types';

filters: [
  {
    universalIdentifier: '...',
    fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER,
    operand: ViewFilterOperand.IS,
    value: ['ACTIVE'],
  },
],

Supported operands per field type

Field typeSupported operands
TEXT, EMAILS, FULL_NAME, ADDRESS, LINKS, PHONES, RAW_JSON, FILES, ACTOR, ARRAYCONTAINS, DOES_NOT_CONTAIN, IS_EMPTY, IS_NOT_EMPTY
ACTOR.source, ACTOR.workspaceMemberIdIS, IS_NOT, IS_EMPTY, IS_NOT_EMPTY
SELECTIS, IS_NOT, IS_EMPTY, IS_NOT_EMPTY
MULTI_SELECTCONTAINS, DOES_NOT_CONTAIN, IS_EMPTY, IS_NOT_EMPTY
RELATIONIS, IS_NOT, IS_EMPTY, IS_NOT_EMPTY
NUMBERIS, IS_NOT, GREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUAL, IS_EMPTY, IS_NOT_EMPTY
RATINGIS, GREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUAL, IS_EMPTY, IS_NOT_EMPTY
CURRENCY, CURRENCY.amountMicrosGREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUAL, IS, IS_NOT, IS_EMPTY, IS_NOT_EMPTY
CURRENCY.currencyCodeIS, IS_NOT, IS_EMPTY, IS_NOT_EMPTY
DATE, DATE_TIMEIS, IS_RELATIVE, IS_IN_PAST, IS_IN_FUTURE, IS_TODAY, IS_BEFORE, IS_AFTER, IS_EMPTY, IS_NOT_EMPTY
BOOLEANIS
UUIDIS
TS_VECTORVECTOR_SEARCH

Field types with similar names can use entirely different operands — SELECT and MULTI_SELECT being a common case.

Value shape per operand

The value field is always a JSON-serializable value, but its expected shape depends on the operand:

Operand familyValue shapeExample
IS, IS_NOT on SELECTarray of option keys (strings)['ACTIVE', 'PENDING']
CONTAINS, DOES_NOT_CONTAIN on MULTI_SELECTarray of option keys (strings)['TAG_A']
IS, IS_NOT on RELATIONarray of record IDs (uuids)['c5a1...']
CONTAINS, DOES_NOT_CONTAIN on text-like fieldsstring'acme'
IS, IS_NOT on NUMBERstring (the value)'5'
IS on RATING / UUIDstring (the value)'5'
GREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUALstring (the bound)'10'
IS, IS_BEFORE, IS_AFTER on DATE / DATE_TIMEISO 8601 string'2025-01-01T00:00:00Z'
IS_EMPTY, IS_NOT_EMPTYempty string''
IS on BOOLEAN'true' or 'false''true'

How views show up in the UI

A view by itself isn't reachable from the sidebar. To make it appear there, pair it with a navigation menu item of type VIEW that points at the view's universalIdentifier. That's the canonical pattern: every custom object typically ships a default view + a sidebar entry that opens it.