Skip to content
Local environment Preproduction — not production data

Factorial Code Forms Customization

When you set only the team and processId, Factorial Code Forms exhibit the following default behavior:

  • The form initiates the process execution.
  • A loading overlay is displayed during execution with the message: “Sending information…”
  • Upon success:
    • The response object is logged in the JavaScript console.
    • The form element is replaced by the message: “The form has been successfully submitted.”
  • Upon error:
    • The response object is logged in the JavaScript error console.
    • The form element is replaced by the message: “There has been an error submitting the form.”

These behaviors can be extended with the following configurations:

You can provide functions to manage the process execution response. This is powerful, allowing actions like retrieving records from a database and showing them to the user.

The syntax for setting these functions in each approach would be:

<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-on-success="(optional) HANDLER_FUNCTION_NAME"
data-fcode-form-on-next-step="(optional) HANDLER_FUNCTION_NAME"
data-fcode-form-on-error="(optional) HANDLER_FUNCTION_NAME"
></div>

Provide a globally available JavaScript function (called with window.${functionName}), that receives the generated formId, and the JSON result of the started execution. For example:

<script>
window.manageOutput = (formId, jsonResponse) => {
document.getElementById("output").innerHTML = JSON.stringify(
jsonResponse,
2,
null
);
};
</script>

The same approach works for the success, next-step and error callbacks.

Default Behaviors Using Response Information

Section titled “Default Behaviors Using Response Information”

We have implemented several default behaviours to handle the most common use cases:

Show a Success Message from Process Execution

Section titled “Show a Success Message from Process Execution”

If your process execution returns a JSON containing a message attribute (that could include HTML code), …

Screenshot

… it will be used if form submission was successfull:

Screenshot

Include validation errors related to the global form or specific fields. These errors are attached to the default ones that our validator already includes.

For example, if you have this form schema specification:

{
"type": "object",
"title": "",
"properties": {
"email": {
"type": "string",
"title": "Your email"
},
"phone": {
"type": "object",
"title": "Your phone contact",
"properties": {
"countryCode": {
"title": "Country code",
"type": "string"
},
"number": {
"title": "Number",
"type": "string"
}
}
}
}
}

And in your process execution return an error response that includes a formErrors object:

return {
status: 400,
body: {
formErrors: {
fields: {
email: "Some validation error in email.",
phone: {
countryCode: "Some validation error in country code.",
},
},
global: ["One global error.", "Other global error."],
},
},
};

It will be shown in the form as a validation error:

Screenshot

If your process execution returns a JSON containing a redirect object with url and optionally timeout, the user will be redirected to that page after form submission:

return {
redirect: {
url: "https://google.com",
timeout: 2000,
},
};

Provide a JSON object that will be used to set initial values in the form. Particularlly useful for setting hidden fields values.

The syntax for setting these defaults in each approach would be:

<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-default-values='{
"company": "Factorial Code",
"oneHiddenField": "the-value"
}'
></div>

Configuring Factorial Code Sync or Async Process Execution

Section titled “Configuring Factorial Code Sync or Async Process Execution”

By default, all process executions are synchronous. However, for long-time executions, consider starting them asynchronously. Factorial Code will respond instantly with a 201 HTTP code and a JSON object containing the execution ID.

To set these default values, use the following syntax for each approach:

<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-async="true"
></div>

By default an embedded form always runs the current version of the process. To pin it to a published process version, pass the version tag. A version alias works too, which lets you move every embed to a new version by repointing the alias, without editing the embedded page.

The version applies to both requests the form makes: loading the form definition and submitting it.

In a multi-step form it carries over to every step. A step result names the next process but not a version for it, so the version you pinned is the only intent available — and it is usually what you want, since the steps of one flow are normally released together. A step whose process has no such version runs its current version.

The process dashboard writes this for you: enable forms, pick a version in the selector next to the embed code, and copy the generated snippet.

<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-process-version="v1.0.0"
></div>

In the same way we support headers on webhooks, we support headers on forms, specially interesting for the initiated by header.

To set these form headers, use the following syntax for each approach:

<div
data-fcode-form-headers='{"my-custom-header": "foo"}'
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
></div>

By default the form embed talks to https://code.factorialhr.com/platform. You can point it at a different backend (for example, a server running fcode http when migrating from Factorial Code Cloud) by setting the host URL.

<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-host-url="https://your-host"
></div>

Factorial Code Forms support additional configuration using a JSON object called options. This object can be configured in the embedded script or function, or in the JSON Schema specification:

<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-options='
{
"theme": "light (default)",
"loadingOverlayDisabled": false,
"loadingOverlayContent": "Sending information...",
"loadingContent": "Loading form...",
}'
></div>

Factorial Code Forms support several appearance configurations to align with your site’s styles.

This configuration must be provided using the options object and allows you to change various appearance details:

  • Theme & styles configuration
  • Disable the loading overlay
  • Change the loading overlay text message
  • Change the message shown while the form is loading
{
"theme": "light (default)",
"loadingOverlayDisabled": false,
"loadingOverlayContent": "Sending information...",
"loadingContent": "Loading form..."
}

Factorial Code Forms include one out-of-the-box theme, defined using a CSS variables file:

Screenshot

You can create a new theme by extending this CSS file and setting the embedFormOptions configuration themeStylesheet, for example:

"embedFormOptions": {
"themeStylesheet": "https://code.factorialhr.com/sdk/styles-theme-custom.css"
},

Another alternative is to provide the CSS rules directly into the themeStylesheet option:

"embedFormOptions": {
"themeStylesheet": ":root {\n--ycf-accent-color: #05e20c;\n--ycf-accent-color-darker: #be0493;}"
}

For adding the same form in several pages, provide a custom class name using the form options:

"embedFormOptions": {
"className": "white-background-form"
}

Factorial Code Forms are rendered using react-jsonschema-form so all the UI schema configuration from this library, is available for use.

To provide greater flexibility to the forms and the content they render (titles, descriptions, help messages), we have enhanced and extended them to support markdown. Read more about this in Factorial Code parameters.”

For example, to change the submit button text, add this node in your parameters schema:

"ui": {
"ui:submitButtonOptions": {
"submitText": "Click me!"
}
}

Another powerful kind of personalization that Factorial Code Forms allow is variable replacement.

This allows defining variables that could differ in each form rendering. These variables are then replaced in the form definition using the mustache syntax.

Inside the additional options config, a variables node can be provided and would be used to replace tokens in all the definition schema, including titles, descriptions, default values, enums, etc.

Example of Variables for Content Customization

Section titled “Example of Variables for Content Customization”

Let’s demonstrate how it works with an example. Suppose you want to implement an upgrade process, and depending on the new plan, you want to display the benefits.

Having these form schema:

{
"title": "Upgrade to {{newPlan}} plan",
"description": "{{#benefits}}* {{.}}\n{{/benefits}}",
"type": "object",
"properties": {
"email": {
"title": "Your email",
"type": "string",
"format": "email"
}
},
"required": ["email"]
}

You could embed it with this approach and different plan configurations:

<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-options='{
"variables": {
"newPlan": "STARTER",
"benefits": [
"Includes 15M Yeps / month",
"Max 10 concurrent executions",
"Up to 10 team members"
]
}
}'
></div>

You can experience diverse form renderings. Visit the samples page to see them in action.

One more feature that opens up a world of posibilities is the $ref variables. With these replacement you can adapt your form schema in each embed situation, with a full replacement of some node of this schema.

Suppose you have an enumeration and you need to use different options in each form embed usage. That’s possible with variables:

Having this form schema:

{
"title": "Upgrade plan",
"type": "object",
"properties": {
"newPlan": {
"title": "Your new plan",
"type": "string",
"enum": {
"$ref": "#/variables/availablePlans"
}
}
},
"required": ["newPlan"]
}

You could embed it with this approach and different available plan configurations:

<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-options='{
"variables": {
"availablePlans": ["STARTER", "GROWTH"],
}
}'
></div>

The variables above are supplied by the embedder or carried forward from a previous step. When the values must be computed on the server before the form is shown — a dropdown loaded from an API, config read from team variables — add a preRenderProcess at the root of the form schema, set to the slug or id of a process that returns a variables node:

{
"title": "Choose your team",
"type": "object",
"preRenderProcess": "load-team-options",
"properties": {
"factorial_team_id": {
"$ref": "#/variables/factorialTeamField"
}
},
"required": ["factorial_team_id"]
}

When the form is opened, Factorial Code runs that process synchronously and merges the variables it returns into the schema, so the $refs resolve to the freshly-computed values. The process must return a variables object:

async function main() {
const { createFactorialClient } = fcode.import("factorial-sdk");
const teams = await createFactorialClient().teams.teams.all();
return {
variables: {
factorialTeamField: {
title: "Factorial team",
type: "string",
oneOf: teams.map((t) => ({ const: String(t.id), title: t.name })),
},
},
};
}
module.exports = { main };

This removes the need for a throwaway first step whose only job was to load data and hand it forward. Because the pre-render runs before any user input, it can only use data that needs none (reference collections, stored variables, config). Any query-string parameters on the form URL are passed to the process as fcode.context.parameters.

Because the form is served only once the pre-render finishes, opening it takes as long as the process takes to run. While that happens the SDK shows a placeholder of the form under a loading spinner — set loadingContent to tell people what is being loaded. If the process fails or times out, the form renders an error message instead and the onError callback is called.

Any visible text in a form schema can be translated. Write fcode.i18n("key") where the text goes, and the platform replaces it with the translation before serving the schema — so the browser receives a schema already written in one language.

The translations themselves live in your workspace’s locales, one YAML file per language. See Internationalization for how to write and manage them.

{
"title": "fcode.i18n(\"signup.title\")",
"type": "object",
"properties": {
"name": {
"title": "fcode.i18n(\"signup.name.label\")",
"type": "string"
},
"email": {
"title": "fcode.i18n(\"signup.email.label\")",
"type": "string",
"format": "email",
"ui": {
"ui:placeholder": "fcode.i18n(\"signup.email.placeholder\")"
}
}
},
"required": ["name", "email"],
"embedFormOptions": {
"loadingOverlayContent": "fcode.i18n(\"signup.overlay\")"
}
}

With signup.title, signup.name.label and the rest defined in each locale:

# en
signup:
title: "Signup form"
name:
label: "Your name"
email:
label: "Your email"
placeholder: "You have to use a business email"
overlay: "Creating new user..."

Arguments are supported too, and fill in %{...} placeholders:

{ "description": "fcode.i18n(\"signup.email.help\", { max: \"120\" })" }

Pass locale in the additional options, or data-fcode-form-locale on the container. It is sent to the platform as the Fcode-Locale header, and the schema is fetched again when it changes.

<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-locale="es"
></div>

When no locale is given, the workspace’s primary locale is used, and it is also the fallback for keys the chosen locale has not translated yet.

Dynamic behaviour while the user fills the form — reformatting a value, or deriving one field from another — belongs in your own page, not in the form schema. A schema is served to every visitor of the form, so it cannot carry executable code.

Use the React onChange prop when you embed @factorialco/fcode-react-forms, or the fcode-forms-* events the SDK dispatches on document when you embed the hosted script:

<script>
document.addEventListener("fcode-forms-on-submit-success", (event) => {
const { formId, formSubmittedData, processExecutionResult } = event.detail;
analytics.track("User Registered", formSubmittedData);
});
</script>

The events are fcode-forms-sdk-init, fcode-forms-init-form, fcode-forms-on-submit-success, fcode-forms-on-next-step and fcode-forms-on-submit-error. For submission results specifically, the success, next-step and error callbacks give you the same information as a direct callback.

Factorial Code Forms can be shown in a modal window after the user clicks on some element. To achieve this, you only need to add all the form configuration to that element, and also include the data attribute data-fcode-form-modal. Here you have one example:

<button
data-fcode-form-modal
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
>
Open the form
</button>