ObjectUIObjectUI
Fields

AutoNumber Field

Read-only auto-generated sequence number

The AutoNumber Field component displays auto-generated sequence numbers. This is a read-only field where the value is automatically generated by the backend when records are created.

Basic Usage

Basic Autonumber

ORD-0001

Custom Format

Invoice Number Format

INV-1234

Date-Based Format

Date Based Ticket Id

TKT-202403-0567

Field Schema

An auto-number field is authored as AutoNumberFieldMetadata (@object-ui/types), which is the source of truth for the key set: it extends BaseFieldMetadata with the record-number pattern, autonumberFormat. The type value is autonumber, the spelling @objectstack/spec's FieldType declares (auto_number is refused at type).

import type { AutoNumberFieldMetadata } from '@object-ui/types';

const invoiceNumber: AutoNumberFieldMetadata = {
  type: 'autonumber',
  name: 'invoice_number',
  label: 'Invoice Number',
  inlineHelpText: 'Assigned by the platform when the record is created.',
  readonly: true,
  autonumberFormat: 'INV-{0000}',
};

autonumberFormat is the spelling @objectstack/spec prefers on a new field. format is the older shorthand for the same pattern, and the spec still accepts it: format: 'INV-{0000}' mints the same numbers. When a field sets both, autonumberFormat wins and format is ignored.

The generated value, and the className a host supplies, are not metadata keys — they are runtime widget props. See Field Widget Props.

Format Templates

Common format patterns:

Simple Sequential

autonumberFormat: '{0000}'           // 0001, 0002, 0003...
autonumberFormat: '{00000}'          // 00001, 00002, 00003...

With Prefix

autonumberFormat: 'ORD-{0000}'       // ORD-0001, ORD-0002...
autonumberFormat: 'INV-{00000}'      // INV-00001, INV-00002...
autonumberFormat: 'CUST-{000}'       // CUST-001, CUST-002...

Date-Based

autonumberFormat: '{YYYY}-{0000}'    // 2024-0001, 2024-0002...
autonumberFormat: '{YY}{MM}-{000}'   // 2403-001, 2403-002...
autonumberFormat: 'ORD-{YYYYMMDD}-{00}'  // ORD-20240315-01...

Mixed Format

autonumberFormat: 'PO-{YYYY}-{MM}-{0000}'  // PO-2024-03-0001

Format Placeholders

These are the tokens parseAutonumberFormat (@objectstack/spec/data) reads:

  • {0}, {00}, {000}, etc. - Sequential number, zero-padded to at least that many digits (one per format)
  • {YYYY} - Four-digit year (2024)
  • {YY} - Two-digit year (24)
  • {MM} - Two-digit month (03)
  • {DD} - Two-digit day (15)
  • {YYYYMMDD} - Year, month and day together (20240315)
  • {field_name} - The value of another field on the same record

The date tokens are exact and case-sensitive, and there are no others: no quarter, week or time token. Any other name in braces is a {field_name} token, so {Q} reads a field called Q, not the quarter. A referenced field must exist on the object and be set before the record is created: objectstack compile refuses a format that names a field the object does not have, and an insert whose referenced field is empty fails rather than mint a number.

Backend Implementation

ObjectUI does not render the format. An ObjectStack backend mints each value when the record is inserted, with the renderer @objectstack/spec/data exports: parseAutonumberFormat reads the format into tokens and renderAutonumber renders them. The query engine and the SQL driver share that renderer, so the placeholders above are the whole grammar. A backend of your own should call the same two functions rather than re-implement the token grammar.

Sequence Management

Allocating the next number is the backend's job, not the renderer's. ObjectUI never generates a value: AutoNumberField displays whatever the saved record already carries, and renders a muted placeholder dash while the field is still empty. What ObjectUI owns is the metadata the backend reads — how each value is formatted. There is no starting-value key: @objectstack/spec's FieldSchema refuses starting_number by name, so where a sequence starts is the backend's counter, not the field's metadata:

import type { AutoNumberFieldMetadata } from '@object-ui/types';

const orderNumber: AutoNumberFieldMetadata = {
  type: 'autonumber',
  name: 'order_number',
  label: 'Order Number',
  autonumberFormat: 'ORD-{YYYY}-{0000}',
};

On the backend, keep one counter per object, field and counter scope, and increment it in the same transaction that inserts the record, so two concurrent inserts cannot read the same current value. The scope is whatever the format renders before the {0000} slot, and renderAutonumber returns it with the value: 'ORD-{YYYY}-{0000}' counts per year and restarts each January, while 'ORD-{0000}' keeps one count.

Because the number is assigned at insert time, it does not exist while the record is still being drafted: a create form shows the field empty, and the value appears once the saved record comes back.

Use Cases

  • Order Management: Order numbers, PO numbers
  • Invoicing: Invoice IDs, receipt numbers
  • Ticketing: Support ticket IDs, case numbers
  • Customer Management: Customer IDs, account numbers
  • Inventory: SKU numbers, serial numbers
  • Document Management: Document IDs, revision numbers

Best Practices

  1. Choose appropriate padding: Use enough digits for expected volume
  2. Include year for long-running systems: Helps with archival and partitioning
  3. Use meaningful prefixes: Makes numbers self-documenting
  4. Don't expose internal IDs: Use auto-numbers for user-facing identifiers
  5. Consider reset policies: Decide if/when sequences reset (yearly, monthly, etc.)

On this page