# @visma/formula

> React component for configurable forms. Optionally connect to the backend to fetch external config and submit form data to.

Latest version **0.4.269** (published 2025-03-26) · ISC license · 0 weekly downloads

## Install

```sh
npm install @visma/formula
pnpm add @visma/formula
yarn add @visma/formula
bun add @visma/formula
```

## Health

**Score 30/100 (F)** — status: maintenance-mode.

Positive: esm support; no vulnerabilities.

Warnings: low downloads; no types; pre 1.0.

Negative: stale; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.4.269 |
| Published | 2025-03-26 |
| First published | 2021-04-26 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | none |
| Module format | ESM + CommonJS |
| Dependencies | 32 |
| Unpacked size | 5.5 MB |
| Known vulnerabilities | 0 (+23 in 1 direct dependencies) |
| Install scripts | no |
| Author | Arno Saine |
| Maintainers | visma_bot, arnosaine, juusoko |

## Links

- npm: https://www.npmjs.com/package/@visma/formula
- Repository: https://github.com/Visma-Consulting/formula
- Homepage: https://github.com/Visma-Consulting/formula#readme
- Issues: https://github.com/Visma-Consulting/formula/issues
- npm.io page: https://npm.io/package/@visma/formula

## Dependencies (32)

- [sift](https://npm.io/package/sift.md) ^15.0.0
- [axios](https://npm.io/package/axios.md) ^0.21.0
- [dayjs](https://npm.io/package/dayjs.md) ^1.11.7
- [immer](https://npm.io/package/immer.md) ^9.0.2
- [lodash](https://npm.io/package/lodash.md) ^4.17.21
- [moment](https://npm.io/package/moment.md) ^2.29.1
- [date-fns](https://npm.io/package/date-fns.md) ^2.22.1
- [@mui/base](https://npm.io/package/@mui/base.md) ^5.0.0-alpha.122
- [base64-js](https://npm.io/package/base64-js.md) ^1.5.1
- [notistack](https://npm.io/package/notistack.md) ^1.0.9
- [react-rte](https://npm.io/package/react-rte.md) ^0.16.5
- [use-axios](https://npm.io/package/use-axios.md) ^0.3.7
- [react-dates](https://npm.io/package/react-dates.md) ^21.8.0
- [pretty-bytes](https://npm.io/package/pretty-bytes.md) ^6.0.0
- [@emotion/core](https://npm.io/package/@emotion/core.md) ^11.0.0
- [@mui/material](https://npm.io/package/@mui/material.md) ^5.11.15
- [@emotion/react](https://npm.io/package/@emotion/react.md) ^11.10.6
- [mui-datatables](https://npm.io/package/mui-datatables.md) ^4.0.0
- [tiny-invariant](https://npm.io/package/tiny-invariant.md) ^1.1.0
- [util-deprecate](https://npm.io/package/util-deprecate.md) ^1.0.2
- [@emotion/styled](https://npm.io/package/@emotion/styled.md) ^10.0.27
- [markdown-to-jsx](https://npm.io/package/markdown-to-jsx.md) ^7.1.2
- [@visma/rjsf-core](https://npm.io/package/@visma/rjsf-core.md) ^3.1.0-101
- [@mui/styled-engine](https://npm.io/package/@mui/styled-engine.md) ^5.11.11
- [@material-ui/styles](https://npm.io/package/@material-ui/styles.md) ^4.11.4
- [@mui/icons-material](https://npm.io/package/@mui/icons-material.md) ^5.15.10
- [@mui/x-date-pickers](https://npm.io/package/@mui/x-date-pickers.md) ^6.0.3
- [react-error-boundary](https://npm.io/package/react-error-boundary.md) ^3.1.0
- [react-google-recaptcha](https://npm.io/package/react-google-recaptcha.md) ^2.1.0
- [@visma/rjsf-material-ui](https://npm.io/package/@visma/rjsf-material-ui.md) ^3.1.0-101
- [react-google-recaptcha-v3](https://npm.io/package/react-google-recaptcha-v3.md) ^1.10.1
- [@visma/react-openapi-client-generator](https://npm.io/package/@visma/react-openapi-client-generator.md) ^0.1.3

## Recent versions

- 0.4.269 (latest) — 2025-03-26
- 0.4.268 — 2025-03-26
- 0.4.267 — 2025-03-25
- 0.4.266 — 2025-02-11
- 0.4.265 — 2025-02-07
- 0.4.264 — 2024-12-12
- 0.4.263 — 2024-12-09
- 0.4.262 — 2024-11-26
- 0.4.261 — 2024-11-15
- 0.4.260 — 2024-11-15
- 0.4.259 — 2024-11-15
- 0.4.258 — 2024-11-12
- 0.4.257 — 2024-09-17
- 0.4.256 — 2024-08-05
- 0.4.255 — 2024-07-30
- … 255 more at https://npm.io/package/@visma/formula/versions

## README

# @visma/formula 🏎

React component for configurable forms. Optionally connect to the backend to fetch external config and submit form data to.

## Requirements

1. Material UI v4, MUI v5 and `react-intl` are required. Install and set up if necessary:

```sh
npm i @visma/formula @emotion/styled @emotion/react @mui/x-date-pickers @mui/base @mui/material @material-ui/core @material-ui/styles @material-ui/icons @material-ui/lab react-intl --legacy-peer-deps
```

2. Add Vite / Webpack alias for `@emotion/core`:

```js
// vite.config.js
//...
export default defineConfig({
   resolve: {
     alias: {
       '@emotion/core': '@emotion/react',
     },
   },
});

// webpack.config.js
module.exports = {
  //...
   resolve: {
     alias: {
       '@emotion/core': '@emotion/react',
     },
   },
};
```
```

## Examples

### Login form

```js
import Formula from '@visma/formula';

<Formula
  config={{
    title: 'Log In',
    elements: [
      {
        key: 'email',
        type: 'email',
        name: 'Email Address',
        required: true,
      },
      {
        key: 'password',
        type: 'password',
        name: 'Password',
        required: true,
      },
    ],
  }}
  onSubmit={({ values }) => console.log(values)}
/>;
```

### Use external config, prefill some fields

```js
import Formula from '@visma/formula';

<Formula
  axios={(axios) => {
    axios.defaults.baseURL = 'https://example.com/formula/api';
    axios.defaults.headers.common.Authorization = 'Bearer <token>';
  }}
  id="1"
  // Assuming form has at least a formGroup with key `customer`, containing
  // fields with keys `firstName` & `lastName`.
  formData={useMemo(
    () => ({
      customer: {
        firstName: user.firstName,
        lastName: user.lastName,
      },
    }),
    [user]
  )}
/>;
```

## Components

### `<Formula>`

#### Props

One of `config`, `id` or `dataId` is required. Rest are optional.

| Name                                              | Type                                                                                                                                                                                                                              | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |
|---------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `config`                                          | [Form](https://visma-consulting.github.io/formula/docs/interfaces/Form.html)                                                                                                                                                      | Form config                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `formData`                                        | `any`                                                                                                                                                                                                                             | Optional, prefilled form data. Ensure the reference does not change undesirably, e.g. using `useMemo`.                                                                                                                                                                                                                                                                                                                                                  |
| `id`                                              | `string`                                                                                                                                                                                                                          | External form config id                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `dataId`                                          | `string`                                                                                                                                                                                                                          | Resume editing                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `onPreSubmit`                                     | `async (args: Args, event: SubmitEvent) => void \                                                                                                                                                                                 | boolean \                                                                                                                                                                                                                                                                                                                                                                                                                                               | [Args, SubmitEvent]` | Run code before submit. Falsy return value prevents the submit. Return `true` or modified args to submit.                                           |
| `onSubmit`                                        | `({ values }) => void`                                                                                                                                                                                                            | Override default submit handler                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `onPostSubmit`                                    | `(dataId, { values }) => void`                                                                                                                                                                                                    | Get `dataId` of submitted form data                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `confirm`                                         | `boolean \                                                                                                                                                                                                                        | { title: ReactElement, description: ReactElement }`                                                                                                                                                                                                                                                                                                                                                                                                     | Show confirm dialog or use object for other messages. Default: `true`                                                                               |
| `axios`                                           | `axios => void`                                                                                                                                                                                                                   | Get access to API client's axios instance e.g. to set defaults                                                                                                                                                                                                                                                                                                                                                                                          |
| `dateFnsLocale`                                   | `Locale` from `date-fns`                                                                                                                                                                                                          | Examples:<br />`import useDateFnsLocale from '@visma/react-app-locale-utils/lib/useDateFnsLocale.js';`<br />`import { fi } from 'date-fns/locale';`                                                                                                                                                                                                                                                                                                     |
| `children`                                        | `ReactElement`                                                                                                                                                                                                                    | Override default submit button. Set `<></>` (empty React Frament) to render nothing.                                                                                                                                                                                                                                                                                                                                                                    |
| `review`                                          | `boolean`                                                                                                                                                                                                                         | Show review after the form has been submitted. Default: `true`                                                                                                                                                                                                                                                                                                                                                                                          |
| `forceReview`                                     | `boolean`                                                                                                                                                                                                                         | Show review directly. Default: `false`                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `reviewProps`                                     | `{ actions: ReactNode, showSuccessText: boolean, highlightSuccessText: boolean, hideNotAnswered: boolean }`                                                                                                                       | actions: Additional action buttons<br/>showSuccessText: show success text and summary in review, default `true`<br/>highlightSuccessText: make summary more noticeable, default `false`<br/>hideNotAnswered: hide not answered fields in review, user can see the full form by unchecking a checkbox, default `false`                                                                                                                                   |
| `fillProps`                                       | `{ actions: ReactNode, disableSteps: boolean, disableResetFormdata: boolean, disableElementButtons: boolean, showScores: boolean, disablePrint: boolean }`                                                                        | actions: Additional action buttons<br/>disableSteps: disables steps when filling the form, default `false`<br/>disableResetFormdata: disable resetting formData to initial formData in disabled fields, default `false`<br/>disableElementButtons: disable all button elements, default `false`<br/>showScores: show scores if required metadata is available, default `false`<br/>disablePrint: disable print button in ConfirmDialog, default `false` |
| `confirmComponent`, `previewField`, `reviewField` | `component`                                                                                                                                                                                                                       | [Customize](#customize)                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `customMessages`                                  | `{ submit: string, reviewSubmitConfirmation: string, confirmDialogTitle: string, confirmDialogConsent: string, confirmDialogPreview: string, confirmDialogSendButton: string, confirmDialogCancelButton: string, error: string }` | Overrides default texts in submit button, confirmation dialog, confirm message and error.                                                                                                                                                                                                                                                                                                                                                               |
| `buttonActions`                                   | `object`                                                                                                                                                                                                                          | Functions for button elements, `{functionKey: (buttonActionProps) => boolean}`                                                                                                                                                                                                                                                                                                                                                                          |

### `<FormulaProvider>`

Provide options for any `<Form>` component in children.

Required to use API hooks.

#### Props

Same as for `<Formula>`, except:

- Without `config`, `id`, `dataId`
- `children: ReactElement`: App, wrapped forms

### `<Form>`

#### Props

`config`, `id`, `dataId` and `children` from `<Formula>`

## Hooks

See [src/api.js](src/api.js) for all API hooks.

### List forms

```js
import { useForms } from '@visma/formula';

function ListForms() {
  const forms = useForms({ status: 'published', visibility: 'public' });
  // ...
}
```

### Form config details

```js
import { useForm } from '@visma/formula';

function FormTitle({ id }) {
  const form = useForm(id);

  return <h1>{form.title}</h1>;
}
```

### Intercept built-in submit function

```js
import { Formula, useMutations } from '@visma/formula';
// ...
const { submit } = useMutations();

<Formula
  onSubmit={async (...args) => {
    try {
      return await submit(...args);
    } catch (error) {
      logger(error);
      throw error;
    }
  }}
  // ...
/>;
```

## Customize

### Confirm dialog (`confirmComponent`)

Example:

```js
import {
  DialogActions,
  DialogContent,
  DialogContentText,
} from '@material-ui/core';
import produce, { original } from 'immer';
import { FormattedMessage, useIntl } from 'react-intl';

export function CustomConfirm({ config, formData, children }) {
  const intl = useIntl();

  // children, the original dialog, is readonly – use produce from immer to make deep immutable changes.
  return produce(children, (children) => {
    const dialogContentElement = children.props.children.find(
      (element) => element && original(element)?.type === DialogContent
    );

    if (config.meta?.showScoreOnPreview && dialogContentElement) {
      dialogContentElement.props.children.splice(
        2,
        0,
        <DialogContentText>
          <FormattedMessage
            defaultMessage="Vastauksesi antavat sinulle {score} pistettä."
            values={{
              score: Math.ceil(Math.random() * config.meta.maxScore),
            }}
          />
        </DialogContentText>
      );
    }

    // Reverse dialog children order 🤪
    children.props.children.reverse();

    // Reverse dialog action button order
    children.props.children
      .find((element) => element && original(element)?.type === DialogActions)
      ?.props.children.reverse();

    // If set, override consent message
    const consentElement = dialogContentElement?.props.children.find(
      (element) => element?.key === 'consent'
    );
    if (consentElement) {
      consentElement.props.label = intl.formatMessage({
        defaultMessage:
          'Kyllä, haluan lähettää tiedot ja osallistua palkinnon arvontaan 🏆',
      });
    }
  });
}
```

### Preview (`previewField`) & Review Field (`reviewField`)

Example:

```js
import produce from 'immer';
import { sortBy } from 'lodash';

export function CustomPreviewField({ formData, uiSchema, children }) {
  const dataElement = children[1];

  // children, the original field, is readonly – use produce from immer to make deep immutable changes.
  return produce(children, (children) => {
    if (uiSchema['ui:options'].element.meta.showScoreOnPreview) {
      const highlight = sortBy(
        uiSchema['ui:options'].element.meta.highlightColors,
        ['scoreGreaterThan']
      )
        .reverse()
        .find(({ scoreGreaterThan }) => scoreGreaterThan < formData);

      if (highlight) {
        children[1] = (
          <div style={{ display: 'flex' }}>
            <div style={{ flex: '1 1' }}>{dataElement}</div>
            <div
              style={{
                height: '1.2rem',
                width: '1.2rem',
                color: highlight.color,
              }}
            >
              <svg
                xmlns="http://www.w3.org/2000/svg"
                className="h-6 w-6"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
                strokeWidth={2}
              >
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
                />
              </svg>
            </div>
          </div>
        );
      }
    }
  });
}
```

## Load library dynamically

1. Call `init` from `@visma/formula/lib/dll` before using the API:

   ```js
   import { init } from '@visma/formula/lib/dll';
   import App from 'components/App';
   import React from 'react';
   import ReactDOM from 'react-dom';

   async function main() {
     await init('https://example.com/formula');

     ReactDOM.render(
       <React.StrictMode>
         <App />
       </React.StrictMode>,
       document.getElementById('root')
     );
   }

   main();
   ```

2. Import the API from `@visma/formula/lib/dll`. Note that all components and hooks are available only using the default export:

   ```js
   import DLL from '@visma/formula/lib/dll';

   <DLL.Formula
     axios={(axios) => {
       axios.defaults.baseURL = 'https://example.com/formula/api';
       axios.defaults.headers.common.Authorization = 'Bearer <token>';
     }}
     id="1"
   />;
   ```
<br>
EXAMPLES
<br>
App wrapped with FormulaProvider

   ```js
    async function main() {
     await init('https://example.com/formula');
   
   
     ReactDOM.render(
       <DLL.FormulaProvider
       axios={(axios) => {
         axios.defaults.baseURL = 'https://example.com/formula';
       }}
     >
       <App />
     </DLL.FormulaProvider>
       , document.getElementById('root'));
   }
   main();
   ```
<br><br>
After wrapping App with the Provider, Formula component can be used anywhere inside the App

FormulaComponent
   ```js
      import {IntlProvider} from "react-intl";
      import DLL from "@visma/formula/lib/dll";
      import React from "react";
      
      const FormulaComponent = (props) => {
        return (
          <IntlProvider locale={'fi-FI'}>
            <DLL.Form
              id={props?.formId}
              dataId={props?.formResponseId}
              credentials={props?.credentials}
              ...
            >
            </DLL.Form>
          </IntlProvider>
        );
      }
      
      export default FormulaComponent;
   ```
<br>
Using FormulaComponent inside App
   
   ```js 
      ...
      <FormulaComponent
        formId={formId}
        formResponseId={formResponseId}
        credentials={formulaToken}
        ...
      />
      ...
   ```

---
_Source: https://npm.io/package/@visma/formula · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
