# How to Create Custom Collections in the Instatic Data Workspace

> Learn how Instatic's data workspace uses a React dialog and createCmsDataTable API to let you easily create custom collections and new data tables.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: how-to-guide
- Published: 2026-08-01

---

**The Instatic data workspace enables custom collection creation through a React dialog component that submits table schemas to a persistence layer, instantiating new data tables via the `createCmsDataTable` API.**

The Instatic admin panel provides a **Data Workspace** for managing dynamic content structures. This article explains how developers and content managers can create custom collections—essentially new database tables with defined schemas—through the coordinated interaction of UI components and persistence hooks. According to the CoreBunch/Instatic source code, this process centers on the `NewTableDialog` component and the `useDataWorkspace` hook.

## Architectural Flow for Creating Custom Collections

The data workspace follows a strict unidirectional flow to ensure type safety and atomicity when creating custom collections.

### Step 1: Initiating Creation via DataSidebar

The entry point begins in [`src/admin/pages/data/DataPage.tsx`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/DataPage.tsx), where the workspace initializes the `useDataWorkspace` hook to load existing tables. The left-hand **DataSidebar** component renders a "Create collection" button that toggles the dialog state.

```tsx
// In src/admin/pages/data/DataPage.tsx
const [newTableDialogOpen, setNewTableDialogOpen] = useState(false);

// Button rendered via DataSidebar
<Button onClick={() => setNewTableDialogOpen(true)}>Create collection</Button>

```

### Step 2: Schema Composition in NewTableDialog

When triggered, [`src/admin/pages/data/components/NewTableDialog/NewTableDialog.tsx`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/components/NewTableDialog/NewTableDialog.tsx) renders a modal that collects the collection name and field definitions. It utilizes the shared **FieldSchemaComposer** component to build the initial schema array before submission.

```tsx
// In src/admin/pages/data/components/NewTableDialog/NewTableDialog.tsx
export function NewTableDialog({ open, onClose, tables, onCreate }) {
  const [name, setName] = useState('');
  const [fields, setFields] = useState<FieldDefinition[]>([]);

  const handleSubmit = async () => {
    await onCreate({
      name,
      slug: slugify(name),
      fields, // Array generated by FieldSchemaComposer
    });
    onClose();
  };

  return (
    <Dialog open={open} onClose={onClose}>
      <DialogTitle>Create collection</DialogTitle>
      <FieldSchemaComposer fields={fields} onChange={setFields} />
      <Button onClick={handleSubmit}>Create</Button>
    </Dialog>
  );
}

```

### Step 3: Persistence via createCmsDataTable

Upon confirmation, the dialog invokes `workspace.createTable()`, which calls `createCmsDataTable()` from the `@core/persistence` layer. This function inserts a new row into the `data_tables` database table and returns the created metadata.

### Step 4: Reactive State Updates

The `useDataWorkspace` hook updates its local `tables` state with the new entry, clears existing rows, and sets the newly created table as selected, ensuring the UI reflects the new collection immediately.

```tsx
// In src/admin/pages/data/hooks/useDataWorkspace.ts
const createTable = async (input: CreateDataTableInput): Promise<DataTable> => {
  setTablesError(null);
  const table = await createCmsDataTable(input); // Persistence layer call
  setTables((current) => [...current, { ...table, rowCount: 0 }]);
  setSelectedTableId(table.id);
  setRows([]);
  setSelectedRowId(null);
  return table;
};

```

## Implementation Code Examples

### Consuming the Workspace Hook

Components access the creation logic through the `useDataWorkspace` hook, which isolates table management from presentation layers.

```tsx
const workspace = useDataWorkspace({ shouldLoadRows: true });

const handleCreate = async (input) => {
  await workspace.createTable(input); // Creates the custom collection
};

```

### Handling Field Defaults

The workspace imports utility functions from [`src/admin/pages/data/utils/fieldDefaults.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/utils/fieldDefaults.ts) to generate default values for new fields, ensuring consistency across collections.

## Key Source Files and Functions

- **[`src/admin/pages/data/DataPage.tsx`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/DataPage.tsx)**: Orchestrates the workspace UI and dialog state management.
- **[`src/admin/pages/data/hooks/useDataWorkspace.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/hooks/useDataWorkspace.ts)**: Core hook exposing `createTable` and managing table/row state.
- **[`src/admin/pages/data/components/DataSidebar/DataSidebar.tsx`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/components/DataSidebar/DataSidebar.tsx)**: Sidebar component containing the creation trigger button.
- **[`src/admin/pages/data/components/NewTableDialog/NewTableDialog.tsx`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/components/NewTableDialog/NewTableDialog.tsx)**: Modal dialog for collection name and schema input.
- **[`src/admin/pages/data/components/FieldSchemaComposer/FieldSchemaComposer.tsx`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/components/FieldSchemaComposer/FieldSchemaComposer.tsx)**: Shared UI component for building field definitions.
- **`@core/persistence` (createCmsDataTable)**: Server-side persistence function that writes to the `data_tables` database.
- **[`src/core/data/duplicateRow.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/data/duplicateRow.ts)**: Utilities for row duplication logic within collections.
- **[`src/admin/pages/data/utils/fieldDefaults.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/utils/fieldDefaults.ts)**: Helper functions for generating default field values.

## Summary

- The **Data Workspace** creates custom collections by combining React UI components with a typed persistence layer.
- **NewTableDialog** handles user input and schema composition before submitting to the `useDataWorkspace` hook.
- The **createCmsDataTable** function in the persistence layer handles the actual database insertion, ensuring atomic table creation.
- State management automatically updates the UI, selects the new collection, and clears stale row data to prevent inconsistencies.
- File paths like [`src/admin/pages/data/components/NewTableDialog/NewTableDialog.tsx`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/components/NewTableDialog/NewTableDialog.tsx) and [`src/admin/pages/data/hooks/useDataWorkspace.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/data/hooks/useDataWorkspace.ts) define the critical integration points.

## Frequently Asked Questions

### What is a custom collection in Instatic?

A custom collection in Instatic represents a dynamically created data table defined in the `data_tables` schema. It consists of a unique slug, display name, and an array of field definitions that determine the structure of content entries stored within that collection.

### How does the FieldSchemaComposer contribute to collection creation?

The **FieldSchemaComposer** component renders the UI for defining initial fields (such as text, number, or boolean types) before the collection is created. It generates the `fields` array passed to `createTable`, ensuring the new collection has a valid schema immediately upon instantiation.

### Is the collection creation process atomic?

Yes. The creation process is atomic because `createCmsDataTable` handles both the database insertion and metadata return in a single operation. The `useDataWorkspace` hook only updates local React state after receiving confirmation from the persistence layer, preventing partial or failed creation states from appearing in the UI.

### Can I customize the collection after initial creation?

Absolutely. After creation, the collection appears in the **DataSidebar** and can be selected for editing. The workspace supports schema modifications through the `FieldSchemaComposer` and allows adding, editing, or deleting rows via the data inspector panel, all synchronized through the same `useDataWorkspace` hook.