Schema?

Form!

Meet uniforms!

Author in 3 points

  1. Software Architect at Vazco
  2. Over 5 years with Meteor in production
  3. Likes tea

Motivation

No aldeed:autoform for React

(that's basically it)

Use every schema
with every theme.

(even your own)

Some numbers

  • 51 contributors
  • 152 versions
  • 424k downloads (core)
  • 1.19m downloads (total)
  • Almost years old!

Demo

1. Schema

import SimpleSchema from 'simpl-schema';

export const schema = new SimpleSchema({
  title: { type: String, max: 200 },
  author: { type: String },
  copies: { type: Number, label: 'Number of copies', min: 0 },
  lastCheckedOut: {
    type: Date,
    label: 'Last date this book was checked out',
    optional: true,
  },
});

2. Bridge

import { SimpleSchema2Bridge } from 'uniforms-bridge-simple-schema-2';

import { schema } from './schema';

export const bridge = new SimpleSchema2Bridge(schema);

3. Form

import { AutoForm } from 'uniforms-???';

import { bridge } from './bridge';

async function onBookSubmit(model) {
  await api.books.save(model);
}

export function BookForm {
  return (
    <AutoForm
      schema={bridge}
      onSubmit={onBookSubmit}
    />
  );
}
uniforms-unstyled
uniforms-semantic
uniforms-material
uniforms-antd
uniforms-bootstrap3
uniforms-bootstrap4

Features

Custom Fields

import { connectField } from 'uniforms';

function Rating({ onChange, value = 0 }) {
  return (
    <div>
      {Array.from({ length: 5 }, (_, index) =>
        <span key={index} onClick={() => onChange(index)}>
          {index <= value ? '★' : '☆'}
        </span>
      )}
    </div>
  );
}

export const RatingField = connectField(Rating);

Anything with onChange and value will work.

Custom Fields in v3

import { useField } from 'uniforms';

export function RatingField({ name }) {
  const { onChange, value = 0 } = useField(name);

  return (
    <div>
      {Array.from({ length: 5 }, (_, index) =>
        <span key={index} onClick={() => onChange(index)}>
          {index <= value ? '★' : '☆'}
        </span>
      )}
    </div>
  );
}

Hooks are opt-in - connectField still works.

Custom Layout

import { AutoForm, AutoField, NumField, TextField } from 'uniforms-???';

import { RatingField } from './RatingField';
import { bridge } from './bridge';

async function onSubmit(model) { /* ... */ }

export function CustomForm() {
  return (
    <AutoForm schema={bridge} onSubmit={onSubmit}>
      {/* AutoField will pick correct field component */}
      <AutoField name="name" />

      {/* Explicitly select component */}
      <TextField name="name" />
      <NumField name="age" />

      {/* Custom fields works the same */}
      <RatingField name="rating" />
    </AutoForm>
  );
}

GraphQL

import { GraphQLBridge } from 'uniforms-bridge-graphql';

import schema from './schema.graphql';

// See docs for more about validator and options.
function validator(model) {
  return model.title ? null : {
    details: [{ name: 'title', message: 'Title!' }],
  };
}

// Extra props for field components.
const options = { 'address.street': { label: 'Street' } };

export const bridge = new GraphQLBridge(
  schema.getType('Customer'),
  validator,
  options,
);
type Address {
  city:   String!
  street: String
}

type Customer {
  address: Address
  secret:  Float
}

JSON Schema

import Ajv from 'ajv';
import { JSONSchemaBridge } from 'uniforms-bridge-json-schema';

import { schema } from './schema';

const ajv = new Ajv({ allErrors: true, useDefaults: true });

function createValidator(schema) {
  const validator = ajv.compile(schema);

  return model => {
    validator(model);
    return validator.errors?.length
      ? { details: validator.errors }
      : null;
  };
}

export const bridge = new JSONSchemaBridge(
  schema,
  createValidator(schema),
);
export const schema = {
  type: 'object',
  properties: {
    name: { type: 'string' },
    experience: {
      type: 'integer',
      minimum: 0,
      maximum: 100,
    },
  },
  required: ['name'],
};

Process

uniforms are 100% open source, backed by Vazco, and developed by Vazco Open Source Group

Open Source

  • 100% dog-fooding
  • 100% need-driven
  • 100% willingness

Organic

(no one was hurt nor forced during development)

(really)

First public version was released on 10 May 2016.

Production ready

First deployed version went live on 2 May 2016.

Version 2 was released on

10 June 2019.

Evolution

Version 3 is due... Soon.

Links

Q&A

Thank you!

Schema? Form! Meet uniforms!

By Radosław Miernik

Schema? Form! Meet uniforms!

Meteor Impact 2020

  • 1,430