OSS-first docs

These docs teach the open system first: contracts, generated surfaces, runtimes, governance, and incremental adoption. Studio shows up as the operating layer on top, not as the source of truth.

AI index

DataViews Runtime Library

The @lssm-tech/lib.contracts-spec/data-views and @lssm-tech/lib.design-system libraries provide the runtime logic and UI components to render DataViews in your application.

Installation

bun add @lssm-tech/lib.contracts-spec @lssm-tech/lib.design-system

DataViewRenderer

The primary component for rendering any DataView. It automatically selects the correct layout (List, Table, Grid, Detail) based on the spec.

import { DataViewRenderer } from '@lssm-tech/lib.design-system';
import { MyUserList } from './specs/users.data-view';

export function UserPage() {
  return (
    <DataViewRenderer
      spec={MyUserList}
      items={users}
      pagination={{ page: 1, pageSize: 20, total: 100 }}
      onPageChange={(page) => fetchPage(page)}
    />
  );
}

Props

  • spec: The DataViewSpec definition.

  • items: Array of data items to render.

  • filters: Current filter state object.

  • onFilterChange: Callback when typed filters change. Renderers emit DataViewFilterValue objects for numeric, percent, currency, temporal, duration, and boolean filters.

  • pagination: Object with page, pageSize, total.

  • onPageChange: Callback when page changes.

  • viewMode / defaultViewMode: Controlled or initial collection mode for specs that allow list, grid, or table projections through view.collection.viewModes.

  • density / defaultDensity: Controlled or initial density for collection renderers. Host apps can seed this from @lssm-tech/lib.personalization while specs can declare view.collection.density and table view.density defaults.

  • dataDepth / defaultDataDepth: Controlled or initial summary/standard/detailed/exhaustive projection. Fields can declare visibility.minDataDepth, and collection specs can opt into view.collection.personalization persistence hints.

Collection Defaults And Data Depth

Use one authored DataViewSpec for list, grid, and table experiences. view.collection declares allowed modes, toolbar controls, pagination defaults, density, data depth, and persistence hints. visibility.minDataDepth lets a field appear only when the renderer is in a detailed or exhaustive mode.

import { defineDataView } from '@lssm-tech/lib.contracts-spec/data-views';
import { defineTranslation } from '@lssm-tech/lib.contracts-spec/translations';

const bundle = defineTranslation({
  meta: { key: 'crm.accounts', version: '1.0.0', domain: 'crm', owners: ['@crm'] },
  locale: 'en',
  messages: { 'Name': { value: 'Name' }, 'Owner': { value: 'Owner' }, 'ARR': { value: 'ARR' }, 'Internal notes': { value: 'Internal notes' } },
});

export const AccountsDataView = defineDataView(bundle, {
  meta: {
    key: 'crm.accounts',
    version: '1.0.0',
    title: 'Accounts',
    description: 'Customer account workspace',
    domain: 'crm',
    entity: 'account',
    owners: ['@crm'],
    tags: ['crm', 'accounts'],
    stability: 'stable',
  },
  source: {
    primary: { key: 'crm.accounts.list', version: '1.0.0' },
  },
  view: {
    kind: 'table',
    fields: [
      { key: 'name', label: 'Name', dataPath: 'name', sortable: true },
      { key: 'owner', label: 'Owner', dataPath: 'owner' },
      { key: 'arr', label: 'ARR', dataPath: 'arr', format: { type: 'currency', currency: 'EUR' } },
      {
        key: 'internalNotes',
        label: 'Internal notes',
        dataPath: 'internalNotes',
        visibility: { minDataDepth: 'detailed' },
      },
    ],
    columns: [
      { field: 'name' },
      { field: 'owner' },
      { field: 'arr' },
      { field: 'internalNotes' },
    ],
    collection: {
      viewModes: {
        defaultMode: 'table',
        allowedModes: ['list', 'grid', 'table'],
      },
      pagination: {
        pageSize: 25,
        pageSizeOptions: [10, 25, 50],
      },
      toolbar: {
        search: true,
        viewMode: true,
        filters: true,
        density: true,
        dataDepth: true,
      },
      density: 'comfortable',
      dataDepth: 'standard',
      personalization: {
        enabled: true,
        persist: {
          viewMode: true,
          density: true,
          dataDepth: true,
          pageSize: true,
        },
      },
    },
  },
});

Personalization Bridge

Resolve user preferences in the host app, then pass ordinary renderer props into DataViewRenderer. The bridge helper lives in @lssm-tech/lib.personalization, so the design-system renderer stays portable for apps that do not use personalization. See the

personalization library guide

for the behavior tracker, analyzer, and DataView preference resolver.

import { DataViewRenderer } from '@lssm-tech/lib.design-system';
import { resolveDataViewPreferences } from '@lssm-tech/lib.personalization/data-view-preferences';

const resolved = resolveDataViewPreferences({
  spec: AccountsDataView,
  preferences: profile.canonical,
  insights,
  record: savedDataViewPreference,
});

<DataViewRenderer
  spec={AccountsDataView}
  items={accounts}
  defaultViewMode={resolved.viewMode}
  defaultDensity={resolved.density}
  defaultDataDepth={resolved.dataDepth}
  pagination={{
    page,
    pageSize: resolved.pageSize ?? 25,
    total,
  }}
  onViewModeChange={(viewMode) => {
    tracker.trackDataViewInteraction({
      dataViewKey: AccountsDataView.meta.key,
      dataViewVersion: AccountsDataView.meta.version,
      action: 'view_mode_changed',
      viewMode,
    });
  }}
  onDensityChange={(density) => {
    tracker.trackDataViewInteraction({
      dataViewKey: AccountsDataView.meta.key,
      action: 'density_changed',
      density,
    });
  }}
  onDataDepthChange={(dataDepth) => {
    tracker.trackDataViewInteraction({
      dataViewKey: AccountsDataView.meta.key,
      action: 'data_depth_changed',
      dataDepth,
    });
  }}
/>;

Entity surface guide

For full list/search/filter/results, detail, and edit workflows, use the

entity surfaces guide

to choose between the EntityWorkspace product shell and lower-level DataViewRenderer composition with RichRef, AdaptivePanel, personalization, and RoleMorph safely.

Agent Prompt

Use this prompt when asking an implementation agent to add a preference-aware collection DataView without breaking package boundaries.

Implement a production DataView for <entity>.

Requirements:
- Define one canonical DataViewSpec with kind 'table', collection viewModes for list/grid/table, pagination defaults, toolbar search/filter/view mode/density/dataDepth controls, and personalization persist hints.
- Mark fields that should only appear in richer experiences with visibility.minDataDepth.
- Render it with DataViewRenderer using plain props from resolveDataViewPreferences.
- Track user interactions with trackDataViewInteraction for view mode, density, data depth, search, filters, sorting, and pagination.
- Keep @lssm-tech/lib.design-system independent from @lssm-tech/lib.personalization.
- Add focused tests for default resolution, invalid-mode fallback, data-depth projection, and behavior-derived preferred view mode.

Query Generation

The DataViewQueryGenerator utility helps translate DataView parameters (filters, sorting, pagination) into query arguments for your backend.

import { DataViewQueryGenerator } from '@lssm-tech/lib.contracts-spec/data-views/query-generator';

const generator = new DataViewQueryGenerator(MyUserList);
const params = {
  pagination: { page: 1, pageSize: 20 },
  filters: {
    role: { kind: 'single', value: 'admin' },
    revenue: { kind: 'range', from: 1000, to: 5000 },
    lastSeenAt: { kind: 'single', value: '2026-04-28T08:30:00Z' }
  }
};

const errors = generator.validateParams(params);
const query = generator.generate(params);

// query.input contains skip/take plus the typed filter payloads.