| title | Field Types Reference |
|---|---|
| description | Complete guide to all ObjectStack field types with examples and configuration options |
ObjectStack supports 35 field types covering text, numbers, dates, selections, relationships, media, calculations, and enhanced types. This guide provides practical examples for each type.
Single-line text input for short strings.
import { Field } from '@objectstack/spec';
// Basic text field
name: Field.text({
label: 'Full Name',
required: true,
maxLength: 255,
})
// Searchable text field
account_name: Field.text({
label: 'Account Name',
searchable: true,
unique: true,
})Configuration Options:
required- Make field mandatorymaxLength- Maximum character limitminLength- Minimum character limitsearchable- Enable full-text searchunique- Enforce uniqueness constraintdefaultValue- Set default value
Multi-line text input for longer content.
description: Field.textarea({
label: 'Description',
maxLength: 5000,
rows: 5, // UI hint for initial height
})Email address field with validation.
email: Field.email({
label: 'Email Address',
required: true,
unique: true,
})ℹ️ Info:
Automatically validates email format:user@domain.com
Website/link field with URL validation.
website: Field.url({
label: 'Website',
placeholder: 'https://example.com',
})Phone number field with format validation.
phone: Field.phone({
label: 'Phone Number',
format: 'international', // or 'us', 'uk'
})Secure password field with encryption.
api_key: Field.password({
label: 'API Key',
encryption: true, // Encrypt at rest
readonly: true,
})
⚠️ Warning:
Password fields are automatically masked in UI and encrypted ifencryption: true
Markdown text editor with preview.
documentation: Field.markdown({
label: 'Documentation',
description: 'Supports full Markdown syntax',
})Raw HTML editor (use with caution).
html_content: Field.html({
label: 'HTML Content',
description: 'Raw HTML - sanitize before rendering',
})
⚠️ Warning:
Always sanitize HTML content before rendering to prevent XSS attacks
WYSIWYG editor with formatting toolbar.
notes: Field.richtext({
label: 'Notes',
description: 'Rich text with formatting, lists, links',
})Supports:
- Bold, italic, underline
- Headings, lists, quotes
- Links and images
- Tables
Numeric field for integers or decimals.
quantity: Field.number({
label: 'Quantity',
min: 0,
max: 1000,
defaultValue: 1,
})
// Decimal numbers
temperature: Field.number({
label: 'Temperature',
precision: 5, // Total digits
scale: 2, // Decimal places
})Monetary value with currency symbol.
annual_revenue: Field.currency({
label: 'Annual Revenue',
precision: 18,
scale: 2,
min: 0,
})Display: $1,234.56 (formatted with currency symbol)
Percentage value (0-100 or 0-1).
probability: Field.percent({
label: 'Win Probability',
min: 0,
max: 100,
scale: 1, // One decimal place
})Display: 75.5%
Date picker (no time component).
due_date: Field.date({
label: 'Due Date',
defaultValue: 'today',
})
birthday: Field.date({
label: 'Birthday',
min: '1900-01-01',
max: 'today',
})Format: YYYY-MM-DD
Date and time picker with timezone support.
created_at: Field.datetime({
label: 'Created At',
readonly: true,
defaultValue: 'now',
})Format: YYYY-MM-DD HH:mm:ss
Time picker (no date component).
meeting_time: Field.time({
label: 'Meeting Time',
format: '24h', // or '12h'
})Format: HH:mm:ss
Checkbox for true/false values.
is_active: Field.boolean({
label: 'Active',
defaultValue: true,
})
is_completed: Field.boolean({
label: 'Completed',
defaultValue: false,
})Display: Checkbox or toggle switch
Dropdown with predefined options.
// Simple options (string array)
priority: Field.select({
label: 'Priority',
options: ['Low', 'Medium', 'High', 'Critical'],
})
// Advanced options (with values and colors)
status: Field.select({
label: 'Status',
options: [
{ label: 'Open', value: 'open', color: '#00AA00', default: true },
{ label: 'In Progress', value: 'in_progress', color: '#FFA500' },
{ label: 'Closed', value: 'closed', color: '#999999' },
],
})
// Multi-select
tags: Field.select({
label: 'Tags',
multiple: true, // Allow multiple selections
options: ['Bug', 'Feature', 'Enhancement', 'Documentation'],
})Configuration:
options- Array of string labels or option objectsmultiple- Allow multiple selections (stores as array)color- Color for badges and charts
Reference to another object (many-to-one).
// Basic lookup
account: Field.lookup('account', {
label: 'Account',
required: true,
})
// Filtered lookup
contact: Field.lookup('contact', {
label: 'Contact',
referenceFilters: ['account_id = $parent.account_id'], // Filter by parent
})
// Multiple lookup
related_cases: Field.lookup('case', {
label: 'Related Cases',
multiple: true, // Many-to-many
})Configuration:
reference- Target object name (snake_case)referenceFilters- Filter criteria for lookup dialogdeleteBehavior-'set_null','cascade', or'restrict'multiple- Allow multiple references
Parent-child relationship with cascade delete.
account: Field.masterDetail('account', {
label: 'Account',
required: true,
deleteBehavior: 'cascade', // Delete children when parent deleted
writeRequiresMasterRead: true, // Security enforcement
})ℹ️ Info:
Master-Detail vs Lookup:
- Master-Detail: Tight coupling, cascade deletes, child inherits security
- Lookup: Loose coupling, set null on delete, independent security
Image upload with preview.
product_image: Field.image({
label: 'Product Image',
multiple: false,
maxFileSize: 5 * 1024 * 1024, // 5MB
acceptedFormats: ['image/jpeg', 'image/png', 'image/webp'],
})
// Multiple images
gallery: Field.image({
label: 'Gallery',
multiple: true,
maxFiles: 10,
})File upload field for any file type.
attachment: Field.file({
label: 'Attachment',
maxFileSize: 25 * 1024 * 1024, // 25MB
acceptedFormats: ['application/pdf', 'application/msword'],
})Profile picture/avatar upload.
profile_picture: Field.avatar({
label: 'Profile Picture',
maxFileSize: 2 * 1024 * 1024, // 2MB
cropAspectRatio: 1, // Square crop
})Calculated field based on expression.
full_name: Field.formula({
label: 'Full Name',
expression: 'CONCAT(first_name, " ", last_name)',
readonly: true,
})
full_address: Field.formula({
label: 'Full Address',
expression: 'CONCAT(street, ", ", city, ", ", state, " ", postal_code)',
})
days_open: Field.formula({
label: 'Days Open',
expression: 'DAYS_BETWEEN(created_at, NOW())',
type: 'number',
})ℹ️ Info:
Formula fields are automatically calculated and readonly. See Formula Functions for available functions.
Aggregate data from related records.
total_opportunities: Field.summary({
label: 'Total Opportunities',
summaryOperations: {
object: 'opportunity',
field: 'amount',
function: 'sum',
},
})
open_cases_count: Field.summary({
label: 'Open Cases',
summaryOperations: {
object: 'case',
field: 'id',
function: 'count',
},
})Available Functions:
count- Count related recordssum- Sum numeric fieldavg- Average numeric fieldmin- Minimum valuemax- Maximum value
Auto-incrementing unique identifier.
account_number: Field.autonumber({
label: 'Account Number',
format: 'ACC-{0000}', // ACC-0001, ACC-0002, etc.
})
case_id: Field.autonumber({
label: 'Case ID',
format: 'CASE-{YYYY}-{00000}', // CASE-2024-00001
})Format Tokens:
{0000}- Zero-padded number{YYYY}- Year{MM}- Month{DD}- Day
GPS coordinates with map display.
coordinates: Field.location({
label: 'Location',
displayMap: true,
allowGeocoding: true, // Convert address to coordinates
})Note:
geolocationis an alternative name for thelocationfield type. Both refer to the same GPS coordinate field.
Data Structure:
{
latitude: 37.7749,
longitude: -122.4194,
altitude: 100, // Optional
accuracy: 10, // Optional (meters)
}Structured address field.
billing_address: Field.address({
label: 'Billing Address',
addressFormat: 'us', // 'us', 'uk', or 'international'
})Data Structure:
{
street: '123 Main St',
city: 'San Francisco',
state: 'CA',
postalCode: '94105',
country: 'United States',
countryCode: 'US',
formatted: '123 Main St, San Francisco, CA 94105',
}Code editor with syntax highlighting.
code_snippet: Field.code('javascript', {
label: 'Code Snippet',
lineNumbers: true,
theme: 'monokai',
})
sql_query: Field.code('sql', {
label: 'SQL Query',
readonly: false,
})Supported Languages:
javascript,typescript,python,java,sql,html,css,json,yaml,markdown, and more
Color picker with multiple formats.
category_color: Field.color({
label: 'Category Color',
colorFormat: 'hex', // 'hex', 'rgb', 'rgba', 'hsl'
presetColors: ['#FF0000', '#00FF00', '#0000FF', '#FFFF00'],
allowAlpha: false,
})
theme_color: Field.color({
label: 'Theme Color',
colorFormat: 'rgba',
allowAlpha: true, // Support transparency
})Star rating field.
priority: Field.rating(3, {
label: 'Priority',
description: '1-3 stars',
})
satisfaction: Field.rating(5, {
label: 'Customer Satisfaction',
allowHalf: true, // Allow 0.5 increments
})Configuration:
- First parameter:
maxRating(default: 5) allowHalf- Allow half-star ratings (e.g., 3.5)
Digital signature capture.
customer_signature: Field.signature({
label: 'Customer Signature',
required: true,
readonly: false,
})Stored as: Base64-encoded image data
Numeric slider for visual value selection.
volume: Field.slider({
label: 'Volume Level',
min: 0,
max: 100,
step: 1,
defaultValue: 50,
})
price_range: Field.slider({
label: 'Price Range',
min: 0,
max: 10000,
step: 100,
marks: { 0: '$0', 5000: '$5K', 10000: '$10K' },
})Configuration:
min- Minimum valuemax- Maximum valuestep- Increment step sizemarks- Optional labeled markers on the slider
QR code / Barcode field for scanning and generation.
product_barcode: Field.qrcode({
label: 'Product Barcode',
format: 'qr', // 'qr', 'barcode', 'ean13', 'code128'
readonly: false,
})
ticket_code: Field.qrcode({
label: 'Event Ticket',
format: 'qr',
autoGenerate: true, // Generate on record creation
})Supported Formats:
qr- QR Code (2D matrix barcode, best for URLs, text, and complex data)barcode- Standard 1D barcode (linear format for simple numeric/text data)ean13- EAN-13 barcode (13-digit product identifier, commonly used in retail)code128- Code 128 barcode (high-density 1D format supporting full ASCII character set)
Use Cases:
- Product SKUs and inventory management (ean13, code128)
- Event tickets and access control (qr)
- Document tracking and verification (qr)
- Shipping labels and logistics (code128)
All fields support these common properties:
{
// Identity
name: 'field_name', // snake_case machine name
label: 'Field Label', // Human-readable label
description: 'Help text', // Tooltip/help text
// Constraints
required: false, // Is mandatory
unique: false, // Enforce uniqueness
defaultValue: null, // Default value
// UI Behavior
hidden: false, // Hide from default UI
readonly: false, // Read-only in UI
searchable: false, // Enable search indexing
// Database
index: false, // Create database index
externalId: false, // Use for upsert operations
encryption: false, // Encrypt at rest
}// ✅ GOOD: snake_case for field names
account_name: Field.text({ label: 'Account Name' })
annual_revenue: Field.currency({ label: 'Annual Revenue' })
// ❌ BAD: camelCase or PascalCase
accountName: Field.text({ label: 'Account Name' })
AnnualRevenue: Field.currency({ label: 'Annual Revenue' })// ✅ GOOD: Require essential fields
name: Field.text({
label: 'Name',
required: true,
maxLength: 255,
})
// ✅ GOOD: Optional fields for flexibility
middle_name: Field.text({
label: 'Middle Name',
required: false,
})// ✅ GOOD: Make key fields searchable
account_name: Field.text({
searchable: true,
label: 'Account Name',
})
// ❌ BAD: Don't index large text fields
description: Field.textarea({
searchable: true, // Can impact performance
})// ✅ GOOD: Use meaningful defaults
status: Field.select({
options: [
{ label: 'Open', value: 'open', default: true },
{ label: 'Closed', value: 'closed' },
],
})
created_at: Field.datetime({
defaultValue: 'now',
readonly: true,
})See the CRM Example for real-world usage of all field types:
- Account: Autonumber, formula, currency, select with colors
- Contact: Master-detail, formula (full_name), avatar, email, phone
- Opportunity: Workflow automation, state machine, percent, datetime
- Case: Rating (satisfaction), SLA tracking, formula calculations
- Task: Code, color, rating, location, signature