Fieldia

Get started

A Fieldia page is a JSON document. You show it with the viewer, or with the binding for your framework, and you give it a data source that loads and saves records.

Install

Pick the package for your framework. Each one brings the core, the fields and the viewer with it.

npm install @fieldia/viewer     # plain JavaScript
npm install @fieldia/react      # React 18 or 19
npm install @fieldia/vue        # Vue 3
npm install @fieldia/angular    # Angular 21 or 22

No bundler? Load the script bundle instead — it sets the global Fieldia. See a script tag below.

A first page

A page names its fields once, then lays them out. This one collects sign-ups, so its data is responses rather than a record:

{
  "fieldia": "0.1",
  "id": "signup",
  "title": "Workshop sign-up",
  "data": { "kind": "responses" },
  "fields": {
    "name":  { "type": "char", "label": "Full name", "required": true },
    "email": { "type": "char", "label": "Email", "required": true },
    "role":  {
      "type": "selection", "label": "Your role",
      "options": [
        { "value": "developer", "label": "Developer" },
        { "value": "other", "label": "Something else" }
      ]
    },
    "other_role": { "type": "char", "label": "Which role?" }
  },
  "layout": {
    "type": "sections", "id": "sections",
    "children": [{
      "type": "section", "id": "you", "columns": 2,
      "children": [
        { "type": "field", "id": "name", "field": "name" },
        { "type": "field", "id": "email", "field": "email" },
        { "type": "field", "id": "role", "field": "role", "widget": "radio" },
        { "type": "field", "id": "other_role", "field": "other_role",
          "invisible": "role != 'other'", "required": true }
      ]
    }]
  }
}

Which role? shows only when someone picks Something else, and it is required only then. The page format describes every part.

Plain JavaScript

import { mountViewer } from '@fieldia/viewer';
import { createMemoryDataSource } from '@fieldia/core';
import page from './signup.page.json';

const viewer = mountViewer(document.getElementById('app'), {
  page,
  dataSource: createMemoryDataSource(),
  skin: 'outlined',
});

mountViewer returns a handle: viewer.form is the live form (values, errors, setValue, subscribe), and viewer.destroy() removes it.

React

import { FieldiaForm } from '@fieldia/react';

export function Signup() {
  return <FieldiaForm page={page} dataSource={dataSource} skin="outlined" />;
}

Vue

<script setup lang="ts">
import { FieldiaForm } from '@fieldia/vue';
</script>

<template>
  <FieldiaForm :page="page" :data-source="dataSource" skin="outlined" />
</template>

Angular

import { Component } from '@angular/core';
import { FieldiaFormComponent } from '@fieldia/angular';

@Component({
  selector: 'app-signup',
  imports: [FieldiaFormComponent],
  template: `<fieldia-form [page]="page" [dataSource]="dataSource" skin="outlined" />`,
})
export class SignupComponent {
  readonly page = page;
  readonly dataSource = dataSource;
}

A script tag

For a page with no build step at all. The bundle holds the core, the fields and the viewer — about 60 KB gzipped.

<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/@fieldia/viewer/bundle/fieldia.js"></script>
<script>
  fetch('/signup.page.json')
    .then((response) => response.json())
    .then((page) => Fieldia.mountViewer(document.getElementById('app'), {
      page,
      dataSource: Fieldia.createMemoryDataSource(),
    }));
</script>

Next

The memory data source keeps everything in the browser — right for trying things, not for keeping them. Data sources shows how to connect your own backend; Fields lists every field type and how it can be shown.