← Back to blog

How to Build a Custom Fields System for Your SaaS

10 min read · March 27, 2026

Every B2B SaaS product eventually gets the same request: "Can we add a custom field for X?" It starts with one customer asking for an Industry dropdown. Then another wants a Contract Value number field. Then an enterprise prospect won't sign unless they can customize your data model.

Building custom fields seems straightforward until you start. This guide covers every approach, the tradeoffs of each, and how to decide whether to build or buy.

Why Custom Fields Are Harder Than They Look

Custom fields touch every layer of your stack:

  • Database: Where do you store fields that don't exist in your schema?
  • Validation: How do you enforce types, required fields, and constraints on dynamic data?
  • UI: How do you render a form for fields that didn't exist when you wrote the code?
  • Multi-tenancy: How do you ensure Tenant A's fields don't leak into Tenant B's view?
  • Performance: How do you query and index data that varies per tenant?
  • Maintenance: How do you handle schema changes, migrations, and backwards compatibility?

Let's look at each database approach.

Approach 1: JSONB Column

The most common modern approach. Add a custom_fields JSONB column to your existing tables.

ALTER TABLE contacts ADD COLUMN custom_fields JSONB DEFAULT '{}';

-- Store values
UPDATE contacts
SET custom_fields = '{"industry": "Healthcare", "contract_value": 50000}'
WHERE id = 'contact-123';

-- Query
SELECT * FROM contacts
WHERE custom_fields->>'industry' = 'Healthcare';

Pros:

  • Simple to implement (one ALTER TABLE)
  • Flexible (any structure per row)
  • PostgreSQL has good JSONB indexing (GIN indexes)

Cons:

  • No schema enforcement at the database level
  • Type coercion is your responsibility
  • Queries become complex with deeply nested data
  • No foreign key constraints on values
  • "You lose the ability to create proper indexes on specific fields, query performance degrades as the JSON grows, and data types or constraints cannot be enforced at the database level" - CockroachDB Forum

Approach 2: Entity-Attribute-Value (EAV)

The classic pattern. Three tables: entities, attributes (field definitions), and values.

CREATE TABLE field_definitions (
  id UUID PRIMARY KEY,
  tenant_id TEXT NOT NULL,
  entity_type TEXT NOT NULL,
  field_key TEXT NOT NULL,
  field_label TEXT NOT NULL,
  field_type TEXT NOT NULL,
  validation JSONB
);

CREATE TABLE field_values (
  id UUID PRIMARY KEY,
  field_definition_id UUID REFERENCES field_definitions(id),
  entity_id TEXT NOT NULL,
  value TEXT NOT NULL
);

Pros:

  • Clean relational model
  • Easy to add/remove fields without schema changes
  • Natural multi-tenancy (filter by tenant_id)

Cons:

  • "Querying becomes a nightmare of self-joins, performance tanks as the attribute table grows, and you essentially give up on relational database benefits" - CockroachDB Forum
  • All values stored as TEXT (type safety lost)
  • Reporting is extremely difficult
  • Even Jira struggles with this: "Too many custom fields can compromise the performance of Jira instances" - Atlassian

Approach 3: Extension Tables

A hybrid approach. Each tenant gets dedicated columns in a shared extension table.

CREATE TABLE contact_extensions (
  contact_id UUID PRIMARY KEY REFERENCES contacts(id),
  tenant_id TEXT NOT NULL,
  field_1 TEXT,
  field_2 TEXT,
  field_3 NUMERIC,
  -- ... up to N fields
);

Pros:

  • Real column types (not everything is TEXT)
  • Standard SQL queries work
  • Better performance than EAV

Cons:

  • "After a year with 20 clients, a users table can have 47 columns, and migrations become risky because you're constantly modifying core tables" - V. Checha
  • Fixed upper limit on custom fields per entity
  • Schema migrations across hundreds of tenants is operationally complex

Approach 4: Salesforce's Approach (Flex Columns)

Salesforce uses a metadata-driven architecture with ~500 generic "flex columns" (Value0 through Value500). All stored as VARCHAR. A Universal Data Dictionary maps logical field names to physical column slots at runtime.

This is the gold standard but requires building an entire metadata layer, query translation engine, and caching system. Salesforce had hundreds of engineers working on this for years.

Source: Cirra - Salesforce Database Architecture

The Build-vs-Buy Decision

Building custom fields in-house typically takes 3-6 months of engineering time. The total cost is 3-4x the original estimate once you include development, maintenance (15-20% of build cost annually), security, and training.

Source: Appinventiv - Build vs Buy

For teams under ~100 engineers, the math rarely favors building infrastructure tools. 71% of tech teams choose off-the-shelf solutions to accelerate time-to-value.

Source: Chameleon - Build vs Buy

Build if:

  • Custom fields are your core product (you ARE the custom fields)
  • You have a dedicated platform engineering team
  • You need deep integration with proprietary systems

Buy if:

  • Custom fields are a feature, not your product
  • You need to ship in days, not months
  • Your engineering team should focus on what makes your product unique

The Alternative: Custom Fields as a Service

Instead of building this infrastructure, you can use an API that handles it for you. Kopra provides:

  • Multi-tenant field management with data isolation
  • TypeScript SDK: npm install @kopra-dev/sdk, 5 lines to embed
  • REST API with 26 endpoints
  • Embeddable field editor that tenants configure themselves
  • Webhooks, audit logging, and theming

Your customers get the custom fields they've been asking for. Your engineering team stays focused on your core product.

import { KopraSDK } from '@kopra-dev/sdk';

const sdk = new KopraSDK({
  currentTenant: 'northstar-staffing',
  getToken: async (req) => {
    const res = await fetch('/api/kopra-token', {
      method: 'POST',
      body: JSON.stringify(req),
    });
    return res.json();
  },
});

await sdk.loadCustomFields('fields', {
  fieldGroupKey: 'customer',
  entityId: 'contact-42',
});

Free tier available at kopra.dev. No credit card required.