Skip to main content

Creating forms

A form consists of Components. A component can be anything, but usually it's a form field like a text field, but it can also be just a block of text.

Each component have a few common properties, like the name, label and the description. See the the list of components for details.

Forms for data input

Fabform provides components for most kinds of data input. The following example demonstrates a form with text, email, url and boolean data.

import React, { useState } from "react";
import {
createForm,
textField,
booleanField,
emailField,
fileField,
urlField,
StatefulFormView,
} from "@fab4m/fab4m";

const form = createForm({
name: textField({
label: "Your name",
description: "Enter your full name",
required: true,
}),
email: emailField({
label: "Your email",
description: "Enter your email address",
}),
website: urlField({
label: "Show us your beautiful website",
}),
picture: fileField({
label: "Upload a picture",
}),
agree: booleanField({
label: "I agree to the terms and conditons",
required: true,
}),
});

export default function FormFields() {
// We will store the submitted profile in this state.
const [profile, changeProfile] = useState(undefined);
// As an added bonus we will load any uploaded images so we can display them.
const [image, changeImage] = useState(undefined);
// The data provided is a validated object with the data.
form.onSubmit((e, submittedData) => {
e.preventDefault();
changeProfile(submittedData);
// Fab4m provides us with a file object that we can then load.
if (submittedData.picture.type.startsWith("image")) {
const reader = new FileReader();
reader.onload = function (e) {
changeImage(e.target.result);
};
reader.readAsDataURL(submittedData.picture);
}
});
return (
<div>
<StatefulFormView form={form} />
{profile && (
<div className="card">
<div className="card__header">
<h3>{profile.name}</h3>
</div>
<div className="card__body">
<div>
{image && (
<img
className="avatar__photo avatar__photo--xl"
src={image}
width="120"
height="120"
/>
)}
</div>
<div>
<strong>{profile.name}</strong>
</div>
<div>
<strong>{profile.email}</strong>
</div>
<div>
<strong>{profile.website}</strong>
</div>
</div>
</div>
)}
</div>
);
}

Handling multiple data

Any component can be defined as a multiple compnent, which means that instead of just collecting one result, an array with results will be collected.

import React, { useState } from "react";
import { createForm, textField, StatefulFormView } from "@fab4m/fab4m";

const form = createForm({
foods: textField({
label: "What would you like to eat?",
minItems: 2,
maxItems: 4,
multiple: true,
required: true,
}),
allergies: textField({
label: "Specify your allergies",
description: "Specify any allergies you have.",
multiple: true,
}),
});

export default function FormFields() {
const [data, changeData] = useState(undefined);
form.onSubmit((e, submittedData) => {
e.preventDefault();
// The submitted data is an object with two arrays, allergies and foods.
changeData(submittedData);
});
return (
<>
<StatefulFormView form={form} />
{data && (
<div>
<strong>Your food selection</strong>
<ul>
{data.foods.map((food, i) => (
<li key={i}>{food}</li>
))}
</ul>
<strong>Allergies</strong>
<ul>
{data.allergies.map((allergy, i) => (
<li key={i}>{allergy}</li>
))}
</ul>
</div>
)}
</>
);
}

Grouping data together

If you have a complex form you might need to group several fields together in a group. Fab4m makes this possible through the group component. When you define your group component you also add all the child components to the group.

Each group component you add will be represented as an object in your submitted data. As with all other components, group components can have multiple values.

import React, { useState } from "react";
import {
createForm,
textField,
integerField,
group,
StatefulFormView,
} from "@fab4m/fab4m";

const form = createForm({
name: textField({ label: "Band name" }),
info: group(
{ label: "Band info" },
{
genre: textField({
label: "Genre",
description: "Genre",
required: true,
}),
active_since: integerField({
label: "Active since",
description: "Enter the year from which the band has been active",
}),
}
),
// Groups can be multiple.
performances: group(
{ label: "Performances", multiple: true, minItems: 1 },
{
city: textField({
label: "City",
description: "Enter your full name",
required: true,
}),
audience_count: integerField({
label: "People in the audience",
required: true,
}),
}
),
});

export default function GroupedFields() {
const [band, changeBand] = useState(undefined);
form.onSubmit((e, submittedData) => {
e.preventDefault();
changeBand(submittedData);
});
return (
<div>
<StatefulFormView form={form} />
{band && (
<>
<h4>{band.name}</h4>
<dl>
{/*The band info group data is represented as an object*/}
<dt>Genre</dt>
<dd>{band.info.genre}</dd>
<dt>Active since</dt>
<dd>{band.info.active_since}</dd>
</dl>
<h5>Performances</h5>
<ul>
{/*Multiple groups are represented as an array of objects.*/}
{band.performances.map((performance, i) => (
<li key={i}>
{performance.city} (in the audience: {performance.count})
</li>
))}
</ul>
</>
)}
</div>
);
}

Page breaks

Large forms can be split up into multiple parts. This is done using the pageBreak component. All data will be validated before each page break.

import React, { useState } from "react";
import {
createForm,
textField,
booleanField,
integerField,
emailField,
content,
pageBreak,
StatefulFormView,
} from "@fab4m/fab4m";

const form = createForm({
name: textField({
label: "Your name",
description: "Enter your full name",
required: true,
}),
email: emailField({ label: "Email" }),
// This is the first page break. The user will be asked to continue here.
// After the user has continued, they can go back to edit the previous
// parts.
break1: pageBreak({}),
age: integerField({ label: "Age" }),
city: textField({ label: "City" }),
// You can add as many page breaks as necessary.
break2: pageBreak({}),
terms: content({}, () => <div>Very long terms and conditions</div>),
agree: booleanField({
label: "I agree to the terms and conditions",
required: true,
}),
});

export default function PageBreaks() {
form.onSubmit((e) => {
e.preventDefault();
});
return <StatefulFormView form={form} />;
}

Changing labels (submit button text etc.)

Each form comes with some default labels that can be overridden, for example the form submit text. This is done by changing the labels setting when creating the form.

import React from "react";
import { createForm, textField, StatefulFormView } from "@fab4m/fab4m";

const form = createForm(
{
name: textField({
label: "Your name",
required: true,
}),
},
{ labels: { submit: "Enter your name" } }
);

export default function PageBreaks() {
form.onSubmit((e) => {
e.preventDefault();
});
return <StatefulFormView form={form} />;
}

These are the labels you can configure:

NameDescriptionDefault value
submitUsed as the text for the submit button."Save"
nextUsed as the text on the next button on multipage forms."Next"
previousUsed as the text on the previous button on multipage forms."Previous"
completeUsed as the complete text on the complete button on multipage forms."Coplete"
requiredUsed for the "Required" label on required form components."Required"