Quickback Docs

Record Layouts

Customize how fields are grouped and displayed on the CMS record detail page with code-defined layouts and user-created views.

Record Layouts

The CMS record detail page groups fields into collapsible sections. By default, fields are auto-grouped by naming heuristics (Identity, Contact Info, Financial, etc.). With record layouts, you control the exact grouping.

There are two layers:

  1. Code-defined layouts — developers configure named layouts in defineTable()
  2. User-created views — end-users create and save custom views via the CMS UI

Code-Defined Layouts

Add a layouts property to your defineTable() config:

export default defineTable(contacts, {
  firewall: { organization: {} },
  crud: { /* ... */ },
  layouts: {
    default: {
      sections: [
        { label: "Contact Info", columns: 2, fields: ["name", "email", "phone", "mobile"] },
        { label: "Address", columns: 2, fields: ["address1", "address2", "city", "state", "zip"] },
        { label: "Internal Notes", collapsed: true, fields: ["notes", "internalNotes"] }
      ]
    },
    compact: {
      sections: [
        { label: "Summary", fields: ["name", "status", "email"] }
      ]
    }
  }
});

Each layout has an ordered list of sections. Each section specifies:

PropertyTypeDefaultDescription
labelstringrequiredSection header text
fieldsstring[]requiredColumn names to display
columns1 | 21Number of columns for field layout
collapsedbooleanfalseWhether the section starts collapsed

Fields not assigned to any section are collected into an "Other Fields" section at the bottom.

Layout Switcher

When a table has multiple named layouts, a dropdown appears in the record detail header. Selections persist per table using localStorage.

If only one layout is defined, it's used automatically without showing a dropdown.

User-Created Custom Views

End-users can create their own record layouts via the CMS UI. These are stored in the database and can be shared with other organization members.

Creating a View

  1. Open any record detail page
  2. Click the + View button in the header
  3. In the view builder dialog:
    • Name your view
    • Add sections and assign fields from a dropdown
    • Set columns (1 or 2) and collapsed state per section
    • Optionally share with your organization
  4. Click Create View

The new view appears in the layout dropdown alongside code-defined layouts.

Editing and Deleting Views

  • Click the gear icon next to the dropdown to edit the current custom view
  • Click the trash icon to delete it (with confirmation)
  • Code-defined layouts cannot be edited or deleted from the CMS

Access Control

OperationWho Can Do It
Create a viewAny member, admin, or owner
Edit/delete own viewsThe creator
Edit/delete any viewAdmins and owners
View shared viewsAll organization members

Setting Up the Custom View Feature

To enable user-created views, add a customView table to your Quickback project:

// quickback/features/cms/custom-view.ts
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
import { defineTable } from "@quickback/compiler";

export const customView = sqliteTable("custom_view", {
  id: text("id").primaryKey(),
  organizationId: text("organization_id").notNull(),
  tableName: text("table_name").notNull(),
  name: text("name").notNull(),
  description: text("description"),
  layoutConfig: text("layout_config").notNull(),
  isShared: integer("is_shared", { mode: "boolean" }).default(false),
});

export default defineTable(customView, {
  displayColumn: "name",
  firewall: { organization: {} },
  crud: {
    list: { access: { roles: ["owner", "admin", "member"] } },
    get: { access: { roles: ["owner", "admin", "member"] } },
    create: { access: { roles: ["owner", "admin", "member"] } },
    update: {
      access: {
        or: [
          { roles: ["owner", "admin"] },
          { roles: ["member"], record: { createdBy: { equals: "$userId" } } }
        ]
      }
    },
    delete: {
      access: {
        or: [
          { roles: ["owner", "admin"] },
          { roles: ["member"], record: { createdBy: { equals: "$userId" } } }
        ]
      },
      mode: "hard"
    }
  },
  guards: {
    createable: ["tableName", "name", "description", "layoutConfig", "isShared"],
    updatable: ["name", "description", "layoutConfig", "isShared"]
  }
});

Compile your project to generate the API endpoints. The CMS will automatically detect the customView table and enable the view builder UI.

Fallback Behavior

ScenarioResult
No layouts config, no custom viewsAuto-grouping by naming heuristics
layouts config definedUses "default" layout or first available
Multiple layoutsDropdown for switching, persisted per table
Custom views createdAppear in dropdown below code-defined layouts

Next Steps

On this page