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
Custom Format
Invoice Number Format
Date-Based Format
Date Based Ticket Id
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-0001Format 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
- Choose appropriate padding: Use enough digits for expected volume
- Include year for long-running systems: Helps with archival and partitioning
- Use meaningful prefixes: Makes numbers self-documenting
- Don't expose internal IDs: Use auto-numbers for user-facing identifiers
- Consider reset policies: Decide if/when sequences reset (yearly, monthly, etc.)