Showing preview only (816K chars total). Download the full file or copy to clipboard to get everything.
Repository: final-form/react-final-form
Branch: main
Commit: fcea1a1af63a
Files: 293
Total size: 748.1 KB
Directory structure:
gitextract_z4w9ccys/
├── .babelrc.js
├── .eslintrc
├── .flowconfig
├── .github/
│ ├── CODEOWNERS
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── ci.yml
│ └── lock.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .travis.yml
├── LICENSE
├── MIGRATION_V7.md
├── README.md
├── docs/
│ ├── api/
│ │ ├── Field.md
│ │ ├── Form.md
│ │ ├── FormSpy.md
│ │ ├── useField.md
│ │ ├── useForm.md
│ │ └── useFormState.md
│ ├── api.md
│ ├── examples/
│ │ ├── chakra.md
│ │ ├── field-level-validation.md
│ │ ├── record-level-validation.md
│ │ ├── simple.md
│ │ ├── submission-errors.md
│ │ ├── subscriptions.md
│ │ └── wizard.md
│ ├── examples.md
│ ├── faq.md
│ ├── getting-started.md
│ ├── migration/
│ │ ├── formik.md
│ │ └── redux-form.md
│ ├── philosophy.md
│ └── types/
│ ├── FieldProps.md
│ ├── FieldRenderProps.md
│ ├── FormProps.md
│ ├── FormRenderProps.md
│ ├── FormSpyProps.md
│ └── FormSpyRenderProps.md
├── eslint.config.mjs
├── examples/
│ ├── async-field-level-validation/
│ │ ├── Spinner.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── async-redux-submission/
│ │ ├── Styles.js
│ │ ├── asyncSubmissionMiddleware.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ ├── registrationDuck.js
│ │ └── store.js
│ ├── async-typeahead-redux/
│ │ ├── GithubUserTypeahead.jsx
│ │ ├── Styles.js
│ │ ├── actions.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ ├── store.js
│ │ └── useKeyword.js
│ ├── auto-save-field-blur/
│ │ ├── AutoSave.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── auto-save-selective-debounce/
│ │ ├── AutoSave.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── auto-save-with-debounce/
│ │ ├── AutoSave.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── calculated-fields/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── chakra/
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── validate.js
│ ├── conditional-fields/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── pickupTimes.js
│ │ └── readme.md
│ ├── credit-card/
│ │ ├── Card.js
│ │ ├── Styles.js
│ │ ├── cardUtils.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── sandbox.config.json
│ ├── custom-validation-engine/
│ │ ├── OnBlurValidation.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── debounced-record-level-validation/
│ │ ├── ErrorWithDelay.js
│ │ ├── README.md
│ │ ├── Styles.js
│ │ ├── index.js
│ │ └── package.json
│ ├── declarative-form-rules/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── downshift-typeahead/
│ │ ├── DownshiftInput.js
│ │ ├── Styles.js
│ │ ├── fruit.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── external-submit/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── field-arrays/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── field-level-validation/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── field-warnings/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── warning-engine.js
│ ├── fields-component/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── focus-first-error/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── validate.js
│ ├── format-on-blur/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── format-string-by-pattern/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── hybrid-sync-async-record-level-validation/
│ │ ├── Spinner.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── independent-error-component-render-props/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── independent-error-component-with-hooks/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── listening-for-external-changes/
│ │ ├── BooleanDecay.js
│ │ ├── ExternalModificationDetector.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── loading-initializing-values/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── loading-saving-reinitializing/
│ │ ├── LoadSaveReinitializeForm.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── material-ui/
│ │ ├── .prettierrc
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── parse-format/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── prefixed-fields/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── readme.md
│ ├── record-level-validation/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── redux/
│ │ ├── FormStateFromRedux.js
│ │ ├── FormStateToRedux.js
│ │ ├── Styles.js
│ │ ├── finalFormDuck.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── store.js
│ ├── reusable-field-groups/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── simple/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── strongly-typed-values-typescript/
│ │ ├── Styles.tsx
│ │ ├── components/
│ │ │ ├── CheckboxInput.tsx
│ │ │ ├── MultiCheckboxInput.tsx
│ │ │ ├── MultiSelectInput.tsx
│ │ │ ├── NumberInput.tsx
│ │ │ ├── RadioInput.tsx
│ │ │ ├── SelectInput.tsx
│ │ │ ├── TextAreaInput.tsx
│ │ │ └── TextInput.tsx
│ │ ├── index.tsx
│ │ └── readme.md
│ ├── styling-with-smooth-ui/
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── submission-errors/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── subscriptions/
│ │ ├── RenderCount.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── third-party-components/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── states.js
│ └── wizard/
│ ├── Styles.js
│ ├── Wizard.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── package-scripts.js
├── package.json
├── rollup.config.mjs
├── src/
│ ├── Field.test.js
│ ├── Field.tsx
│ ├── FormSpy.test.js
│ ├── FormSpy.tsx
│ ├── ReactFinalForm.test.js
│ ├── ReactFinalForm.tsx
│ ├── context.test.js
│ ├── context.ts
│ ├── getValue.test.js
│ ├── getValue.ts
│ ├── getters.ts
│ ├── index.ts
│ ├── isReactNative.ts
│ ├── isSyntheticEvent.ts
│ ├── renderComponent.test.js
│ ├── renderComponent.ts
│ ├── shallowEqual.test.js
│ ├── shallowEqual.ts
│ ├── testUtils.ts
│ ├── types.ts
│ ├── useConstant.ts
│ ├── useConstantCallback.test.js
│ ├── useConstantCallback.ts
│ ├── useField.dynamic-name-869.test.js
│ ├── useField.test.js
│ ├── useField.ts
│ ├── useForm.test.js
│ ├── useForm.ts
│ ├── useFormState.test.js
│ ├── useFormState.ts
│ ├── useLatest.ts
│ └── useWhenValueChanges.ts
├── tsconfig.build.json
├── tsconfig.json
├── tslint.json
└── typescript/
├── Field.test.tsx
├── FormSpy.test.tsx
├── ReactFinalForm.test.tsx
├── index.d.ts
├── tsconfig.json
├── useField.test.tsx
└── useFormState.test.tsx
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc.js
================================================
const { NODE_ENV } = process.env;
const test = NODE_ENV === "test";
const loose = true;
module.exports = {
presets: [
[
"@babel/preset-env",
{
loose,
...(test ? { targets: { node: "8" } } : {}),
},
],
"@babel/preset-react",
"@babel/preset-typescript",
],
plugins: [
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-import-meta",
test && "@babel/plugin-transform-react-jsx-source",
].filter(Boolean),
};
================================================
FILE: .eslintrc
================================================
{
"extends": "react-app",
"plugins": ["react-hooks"],
"rules": {
"jsx-a11y/href-no-hash": 0,
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"import/no-anonymous-default-export": 0
}
}
================================================
FILE: .flowconfig
================================================
[ignore]
dist
[include]
[libs]
[options]
================================================
FILE: .github/CODEOWNERS
================================================
* @erikras
================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of
experience, nationality, personal appearance, race, religion, or sexual identity
and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, or to ban temporarily or permanently any
contributor for other behaviors that they deem inappropriate, threatening,
offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at rasmussenerik@gmail.com. The project
team will review and investigate all complaints, and will respond in a way that
it deems appropriate to the circumstances. The project team is obligated to
maintain confidentiality with regard to the reporter of an incident. Further
details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing
Thanks for your interest in contributing to 🏁 React Final Form! Please take a
moment to review this document **before submitting a pull request**.
We are open to, and grateful for, any contributions made by the community.
## Reporting issues and asking questions
Before opening an issue, please search
the [issue tracker](https://github.com/final-form/react-final-form/issues) to
make sure your issue hasn’t already been reported.
**We use the issue tracker to keep track of bugs and improvements** to 🏁 React
Final Form itself, its examples, and the documentation. We encourage you to open
issues to discuss improvements, architecture, internal implementation, etc. If a
topic has been discussed before, we will ask you to join the previous
discussion.
For support or usage questions, please search and ask on
[StackOverflow with a `react-final-form` tag](https://stackoverflow.com/questions/tagged/react-final-form).
We ask you to do this because StackOverflow has a much better job at keeping
popular questions visible. Unfortunately good answers get lost and outdated on
GitHub.
**If you already asked at StackOverflow and still got no answers, post an issue
with the question link, so we can either answer it or evolve into a bug/feature
request.**
## Sending a pull request
**Please ask first before starting work on any significant new features.**
It's never a fun experience to have your pull request declined after investing a
lot of time and effort into a new feature. To avoid this from happening, we
request that contributors create
[an issue](https://github.com/final-form/react-final-form/issues) to first
discuss any significant new features.
Please try to keep your pull request focused in scope and avoid including
unrelated commits.
After you have submitted your pull request, we’ll try to get back to you as soon
as possible. We may suggest some changes or improvements.
Please format the code before submitting your pull request by running:
```sh
npm run precommit
```
## Coding standards
Our code formatting rules are defined in
[.eslintrc](https://github.com/final-form/react-final-form/blob/master/.eslintrc).
You can check your code against these standards by running:
```sh
npm start lint
```
To automatically fix any style violations in your code, you can run:
```sh
npm run precommit
```
## Running tests
You can run the test suite using the following commands:
```sh
npm test
```
Please ensure that the tests are passing when submitting a pull request. If
you're adding new features to 🏁 React Final Form, please include tests.
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: erikras
patreon: erikras
open_collective: final-form
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with a single custom sponsorship URL
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
<!--
👋 Hey, thanks for taking an interest in 🏁 React Final Form!
-->
### Are you submitting a **bug report** or a **feature request**?
<!-- For support request, please use Stack Overflow instead. This issue tracker is reserved for bugs and features. -->
### What is the current behavior?
<!-- If this is a bug, please include steps to reproduce and a minimal demo of the problem using Sandbox, Plunkr, WebpackBin or JSFiddle. -->
### What is the expected behavior?
### Sandbox Link
<!-- Problems are much easier to understand and debug if they can be demonstrated in a minimal environment. -->
### What's your environment?
<!-- Include 🏁 React Final Form version, 🏁 Final Form version, OS/browser affected, Node version, etc. -->
### Other information
<!-- Include here any detailed explanation, stacktraces, related issues, links for Stack Overflow, Twitter, etc. -->
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
👋 Hey, thanks for your interest in contributing to 🏁 React Final Form!
**Please ask first before starting work on any significant new features.**
It's never a fun experience to have your pull request declined after investing a
lot of time and effort into a new feature. To avoid this from happening, we
request that contributors create an issue to first discuss any significant new
features.
Please try to keep your pull request focused in scope and avoid including
unrelated commits.
After you have submitted your pull request, we’ll try to get back to you as soon
as possible. We may suggest some changes or improvements.
Please format the code before submitting your pull request by running:
```
npm run precommit
```
https://github.com/final-form/react-final-form/blob/master/.github/CONTRIBUTING.md
-->
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on: [push]
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node_version }}
uses: actions/setup-node@v2
with:
node-version: "22"
- name: Prepare env
run: yarn install --ignore-scripts --frozen-lockfile
- name: Run linter
run: yarn start lint
prettier:
name: Prettier Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node_version }}
uses: actions/setup-node@v2
with:
node-version: "22"
- name: Prepare env
run: yarn install --ignore-scripts --frozen-lockfile
- name: Run prettier
run: yarn start prettier
test:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node_version }}
uses: actions/setup-node@v2
with:
node-version: "22"
- name: Prepare env
run: yarn install --ignore-scripts --frozen-lockfile
- name: Run unit tests
run: yarn start test
- name: Run code coverage
uses: codecov/codecov-action@v2.1.0
================================================
FILE: .github/workflows/lock.yml
================================================
name: "Lock Threads"
on:
schedule:
- cron: "0 * * * *"
workflow_dispatch:
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
with:
issue-inactive-days: "365"
issue-lock-reason: "resolved"
pr-inactive-days: "365"
pr-lock-reason: "resolved"
================================================
FILE: .gitignore
================================================
.vscode
*.iml
.nyc_output
coverage
flow-coverage
node_modules
dist
lib
es
npm-debug.log
.DS_Store
.yalc
yalc.lock
================================================
FILE: .prettierignore
================================================
coverage
dist
node_modules
================================================
FILE: .prettierrc
================================================
{
"trailingComma": "all"
}
================================================
FILE: .travis.yml
================================================
sudo: false
language: node_js
before_install:
- npm install -g npm@6.4.0
cache:
directories:
- node_modules
notifications:
email: false
node_js:
- "10"
- "12"
- "14"
script:
- npm start validate
after_success:
- npx codecov
branches:
only:
- main
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 Erik Rasmussen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: MIGRATION_V7.md
================================================
# Migration Guide: react-final-form v6 → v7
## Overview
Version 7.0.0 includes a complete TypeScript rewrite (migrated from Flow). While the runtime behavior remains largely unchanged, there are several TypeScript-specific breaking changes you need to be aware of.
## Breaking Changes
### 1. FormState Properties Now Optional
In v7.0.0, most FormState boolean properties can be `undefined`:
**❌ Before (v6.x):**
```typescript
const { dirty, pristine, valid } = formState;
if (dirty && !pristine) { // Works fine
// ...
}
```
**✅ After (v7.0.0):**
```typescript
const { dirty, pristine, valid } = formState;
if ((dirty ?? false) && !(pristine ?? true)) { // Must handle undefined
// ...
}
```
**Affected properties:**
- `dirty`, `pristine`, `valid`, `invalid`
- `dirtySinceLastSubmit`, `modifiedSinceLastSubmit`
- `submitFailed`, `submitSucceeded`, `submitting`, `validating`
- `hasSubmitErrors`, `hasValidationErrors`
**Note:** `values` is still guaranteed to be defined.
### 2. FieldMetaState Type No Longer Exported
**❌ Before (v6.x):**
```typescript
import { FieldMetaState } from 'react-final-form';
const meta: FieldMetaState = { /* ... */ };
```
**✅ After (v7.0.0):**
```typescript
import { FieldRenderProps } from 'react-final-form';
const meta: FieldRenderProps<any>['meta'] = { /* ... */ };
// Or define it locally:
type FieldMetaState = {
active?: boolean;
data?: Record<string, any>;
dirty?: boolean;
// ... etc
};
```
### 3. AnyObject Type No Longer Exported
**❌ Before (v6.x):**
```typescript
import { AnyObject } from 'react-final-form';
```
**✅ After (v7.0.0):**
```typescript
// Define locally:
type AnyObject = Record<string, any>;
```
### 4. UseFieldConfig No Longer Generic
**❌ Before (v6.x):**
```typescript
const config: UseFieldConfig<string> = {
validate: (value) => value ? undefined : 'Required'
};
```
**✅ After (v7.0.0):**
```typescript
const config: UseFieldConfig = {
validate: (value) => value ? undefined : 'Required'
};
```
### 5. FormProps No Longer Accepts Arbitrary Props
In v6.x, you could pass arbitrary props (like `style`, `className`) directly to `<Form>`. In v7.0.0, this is no longer supported due to stricter TypeScript typing.
**❌ Before (v6.x):**
```tsx
<Form
onSubmit={handleSubmit}
style={{ padding: '20px' }}
className="my-form"
>
{/* ... */}
</Form>
```
**✅ After (v7.0.0):**
```tsx
<Form onSubmit={handleSubmit}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit} style={{ padding: '20px' }} className="my-form">
{/* ... */}
</form>
)}
</Form>
// Or wrap in a div:
<div style={{ padding: '20px' }} className="my-form">
<Form onSubmit={handleSubmit}>
{/* ... */}
</Form>
</div>
```
## final-form v5.0.0 Changes
If you're also upgrading final-form to v5.0.0, be aware of these changes:
### 1. InternalFormState Requires asyncErrors
**❌ Before (v4.x):**
```typescript
const mockFormState: InternalFormState = {
values: {},
// ...
};
```
**✅ After (v5.0.0):**
```typescript
const mockFormState: InternalFormState = {
values: {},
asyncErrors: {}, // Now required
// ...
};
```
### 2. Mutator Type Signature Changed
**❌ Before (v4.x):**
```typescript
const mutator: Mutator = (args, state, tools) => {
// ...
};
```
**✅ After (v5.0.0):**
```typescript
// If you get type errors with existing mutators:
const mutator = ((args, state, tools) => {
// ...
}) as unknown as Mutator;
```
## Migration Strategy
For a medium to large codebase, expect to modify 100+ files. Here's a recommended approach:
1. **Update dependencies:**
```bash
npm install react-final-form@^7.0.0 final-form@^5.0.0
```
2. **Fix compilation errors in this order:**
- Handle optional boolean properties (use `?? false` or `?? true`)
- Replace `FieldMetaState` imports with `FieldRenderProps['meta']`
- Replace `AnyObject` imports with local type definition
- Remove generic from `UseFieldConfig<T>` → `UseFieldConfig`
- Fix `<Form>` props (move styling to wrapper or inner `<form>`)
3. **Test thoroughly:**
- All form submissions
- Validation behavior
- Field state management
- Meta information display
4. **Update mocks/tests:**
- Add `asyncErrors: {}` to InternalFormState mocks
- Cast mutators if needed
## Need Help?
If you encounter issues during migration:
1. Check the [TypeScript examples](https://github.com/final-form/react-final-form/tree/main/examples/typescript)
2. Review [closed issues](https://github.com/final-form/react-final-form/issues?q=is%3Aissue+typescript)
3. Open a [new issue](https://github.com/final-form/react-final-form/issues/new) with a reproduction
## Benefits of v7.0.0
Despite the migration effort, v7.0.0 brings significant benefits:
- **Better TypeScript support** - First-class TypeScript instead of generated types from Flow
- **Improved type inference** - Better autocomplete and type checking
- **Modern codebase** - Easier for contributors to work with
- **Long-term maintainability** - TypeScript ecosystem is more active than Flow
---
**Version**: 7.0.0
**Last Updated**: 2026-02-13
================================================
FILE: README.md
================================================
# 🏁 React Final Form
[](https://final-form.org/react)
[](#backers) [](#sponsors) [](https://www.npmjs.com/package/react-final-form)
[](https://www.npmjs.com/package/react-final-form)
[](https://travis-ci.org/final-form/react-final-form)
[](https://codecov.io/gh/final-form/react-final-form)
[](https://github.com/prettier/prettier)
✅ Zero dependencies (that affect your bundle size)
✅ Only peer dependencies: React and
[🏁 Final Form](https://github.com/final-form/final-form#-final-form)
✅ Opt-in subscriptions - only update on the state you need!
✅ 💥 [**3.0k gzipped**](https://bundlephobia.com/result?p=react-final-form) 💥
---
[<img align="right" src="docs/sencha.svg" height="100"/>](https://www.sencha.com/)
### React Final Form is sponsored by [Sencha](https://www.sencha.com/).
Comprehensive JS framework and UI components for building enterprise-grade web apps.
---
## 💬 [Give Feedback on React Final Form](https://goo.gl/forms/dxdfxKNy64DLb99z2) 💬
In the interest of making 🏁 React Final Form the best library it can be, we'd love your thoughts and feedback.
[Take a quick survey](https://goo.gl/forms/dxdfxKNy64DLb99z2).
---
React Final Form is a thin React wrapper for [Final Form](https://final-form.org), which is a subscriptions-based form state management library that uses the [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern), so only the components that need updating are re-rendered as the form's state changes.
## [Getting Started](https://final-form.org/docs/react-final-form/getting-started)
## 🔄 Upgrading from v6 to v7?
See the [Migration Guide](./MIGRATION_V7.md) for TypeScript-specific breaking changes and how to handle them.
## [Philosophy](https://final-form.org/docs/react-final-form/philosophy)
## [Examples](https://final-form.org/docs/react-final-form/examples)
## [API](https://final-form.org/docs/react-final-form/api)
## [FAQ](https://final-form.org/docs/react-final-form/faq)
<img src="https://static.scarf.sh/a.png?x-pxid=8feec529-43ac-4b78-8ad9-17c280b4f2d9" />
================================================
FILE: docs/api/Field.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/Field). Links may not work on Github.com.
# `<Field/>`
```ts
import { Field } from 'react-final-form'
```
A component that registers a field with the containing form, subscribes to field state, and injects both field state and callback functions, `onBlur`, `onChange`, and `onFocus` via a render prop.
The `<Field/>` will rerender any time the field state it is subscribed to changes. By default it subscribes to _all_ field state. You can control which field state it subscribes to with the `subscription` prop.
## Props
`<Field/>` accepts [`FieldProps`](../types/FieldProps) and will call the render function with [`FieldRenderProps`](../types/FieldRenderProps).
The only two required props are [`name`](../types/FieldProps#name) and one of [`component`](../types/FieldProps#component), [`render`](../types/FieldProps#render), or [`children`](../types/FieldProps#children).
## Basic Usage
You need to do three things when using `<Field/>`:
### 1. Provide a `name` prop
The name of the field can be a reference to a "deep" value via [dot-and-bracket syntax](/docs/final-form/field-names), e.g. `'clients[0].address.street'`.
### 2. Provide a way to render the field
There are four ways to render a `<Field/>` component:
| Prop | Type |
| -------------------- | ---------------------------------- |
| `<Field component/>` | `'input' or 'select' or 'textarea'` |
| `<Field component/>` | `React.ComponentType` |
| `<Field render/>` | `Function` |
| `<Field children/>` | `Function` |
The only important distinction is that if you pass a component to the `component` prop, it will be rendered with [`React.createElement()`](https://reactjs.org/docs/react-api.html#createelement), resulting in your component actually being in the React node tree, i.e. inspectable in [DevTools](https://github.com/facebook/react-devtools#react-developer-tools-).
### 3. Connect the callbacks to your input
If you are using `component="input"` (or `select` or `textarea`), `<Field/>` will do this step for you.
```tsx
<Field name="myField" component="input" />
```
But if you are using a custom component or a render prop, you will need to do this yourself.
`<Field/>` makes this as easy as possible by bundling all of the props that your input component needs into one object prop, called `input`, which contains [`name`](../types/FieldRenderProps#inputname), [`onBlur`](../types/FieldRenderProps#inputonblur), [`onChange`](../types/FieldRenderProps#inputonchange), [`onFocus`](../types/FieldRenderProps#inputonfocus), and [`value`](../types/FieldRenderProps#inputvalue).
#### HTML Inputs
If you're going to be using one of the standard HTML inputs, `<input>`, `<select>`, or `<textarea>`, it's just a matter of using [the spread operator](https://reactjs.org/docs/jsx-in-depth.html#spread-attributes) to populate the props of the input.
```tsx
<Field name="myField">
{props => (
<div>
<input {...props.input} />
</div>
)}
</Field>
```
#### Custom Inputs
The only thing a custom input needs to do to be compatible with React Final Form is to accept a `value` prop and somehow call the `onChange` callback to change the value.
```tsx
import TextField from '@material-ui/core/TextField'
...
<Field name="myField">
{props => (
<div>
<TextField
name={props.input.name}
value={props.input.value}
onChange={props.input.onChange}
/>
</div>
)}
</Field>
```
Note: To use an `array` for the values (with another field type, like a tags-input component), you can do `value={[...props.input.value]}` to avoid "Invalid prop type of 'string' warning"
Now, [let's look at some examples](../examples)!
================================================
FILE: docs/api/Form.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/Form). Links may not work on Github.com.
# `<Form/>`
```ts
import { Form } from 'react-final-form'
```
A component that surrounds your entire form and manages the form state. It can inject form state and functionality, e.g. a `handleSubmit` function for you to pass to your `<form>` element, via render props.
On mount, `<Form/>` creates a Final Form [`form` instance](/docs/final-form/types/FormApi), subscribes to changes on that `form`, and places it into the [React Context](https://reactjs.org/docs/context.html) so that the [`<Field/>`](Field) and [`<FormSpy/>`](FormSpy) components can see it.
The `<Form/>` will rerender any time the form state it is subscribed to changes. By default it subscribes to _all_ form state. You can control which form state it subscribes to with the `subscription` prop.
## Props
`<Form/>` accepts [`FormProps`](../types/FormProps) and will call the render function with [`FormRenderProps`](../types/FormRenderProps).
The only two required props are [`onSubmit`](../types/FormProps#onsubmit) and one of [`component`](../types/FormProps#component), [`render`](../types/FormProps#render), or [`children`](../types/FormProps#children).
## Basic Usage
You need to do three things when using `<Form/>`:
### 1. Provide an `onSubmit` prop
`onSubmit` is a function that will be called with the values of your form when the user submits the form _and_ all validation passes. Your `onSubmit` function will not be called if there are validation errors.
### 2. Provide a way to render the form
There are three ways to render a `<Form/>` component:
| Prop | Type |
| ------------------- | --------------------- |
| `<Form component/>` | `React.ComponentType` |
| `<Form render/>` | `Function` |
| `<Form children/>` | `Function` |
The only important distinction is that if you pass a `component` prop, it will be rendered with [`React.createElement()`](https://reactjs.org/docs/react-api.html#createelement), resulting in your component actually being in the React node tree, i.e. inspectable in [DevTools](https://github.com/facebook/react-devtools#react-developer-tools-).
While using `component` might feel easiest if you are migrating from [Redux Form's Higher Order Component](https://redux-form.com/8.2.2/docs/api/reduxform.md/) model, best practice recommends using a render prop.
### 3. Do something with `handleSubmit`
The most important thing that `<Form/>` will pass to your render function is the `handleSubmit` function. `handleSubmit` is a convenience method designed to be passed as the `onSubmit` prop to an HTML `<form>` component. `handleSubmit` will call `event.preventDefault()` to stop the default browser submission process.
In practice, your form will always look something like this:
<!-- prettier-ignore -->
```jsx
<Form onSubmit={onSubmit}>
{props => (
<form onSubmit={props.handleSubmit}>
... fields go here...
<button type="submit">Submit</button>
</form>
)}
</Form>
```
Now, [let's look at adding some `<Field/>`](Field) components!
================================================
FILE: docs/api/FormSpy.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/FormSpy). Links may not work on Github.com.
# `<FormSpy/>`
```ts
import { FormSpy } from 'react-final-form'
```
A component that subscribes to form state, and injects both form state and the `form` instance via a render prop.
The `<FormSpy/>` will rerender any time the form state it is subscribed to changes. By default it subscribes to _all_ form state. You can control which form state it subscribes to with the `subscription` prop.
By providing an `onChange` prop, `<FormSpy/>` can also be used to execute code when a particular part of form state changes.
## Props
`<FormSpy/>` accepts [`FormSpyProps`](../types/FormSpyProps) and will call the render function with [`FormSpyRenderProps`](../types/FormSpyRenderProps).
The only required prop is one of `onChange`, `component`, `render`, or `children`.
## Basic Usage
It should be noted that `<FormSpy/>` is for very advanced use cases.
**If you are not restricting your form state by providing a `subscription` prop to `<Form/>`, you probably do not need `<FormSpy/>`!** Just use the form state injected by `<Form/>`.
You need to do _one_ of two things when using `<FormSpy/>`:
### 1. Provide a way to render the form state
There are three ways to render a `<FormSpy/>` component:
| Prop | Type |
| ---------------------- | --------------------- |
| `<FormSpy component/>` | `React.ComponentType` |
| `<FormSpy render/>` | `Function` |
| `<FormSpy children/>` | `Function` |
The only important distinction is that if you pass a `component` prop, it will be rendered with [`React.createElement()`](https://reactjs.org/docs/react-api.html#createelement), resulting in your component actually being in the React node tree, i.e. inspectable in [DevTools](https://github.com/facebook/react-devtools#react-developer-tools-).
```tsx
// Render a reset button that will be
// disabled when the form is pristine
<FormSpy subscription={{ pristine: true }}>
{props => (
<button
type="button"
disabled={props.pristine}
onClick={() => props.form.reset()}
>
Reset
</button>
)}
</FormSpy>
```
### 2. Pass an `onChange` callback
`<FormSpy/>` can sometimes be useful to execute code when a particular part of form state changes. This is what the `onChange` callback is for.
**If you pass `onChange`, nothing will be rendered.**
```tsx
<FormSpy
subscription={{ valid: true }}
onChange={props => {
console.log('Form validity changed to', props.valid)
}}
/>
```
================================================
FILE: docs/api/useField.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/useField). Links may not work on Github.com.
# `useField()`
```ts
import { useField } from 'react-final-form'
```
<!-- prettier-ignore -->
```ts
(name: string, config: UseFieldConfig) => FieldRenderProps
```
The `useField()` hook takes two parameters:
### `name`
```ts
string
```
**Required**
The name of the field.
### `config`
```ts
UseFieldConfig
```
Optional.
An object that looks just like [`FieldProps`](../types/FieldProps), except without the name.
`useField()` returns [`FieldRenderProps`](../types/FieldRenderProps). It will manage the rerendering of any component you use it in, i.e. the component will only rerender if the field state subscribed to via `useField()` changes.
`useField()` is used internally inside [`<Field/>`](Field).
================================================
FILE: docs/api/useForm.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/useForm). Links may not work on Github.com.
# `useForm()`
```ts
import { useForm } from 'react-final-form'
```
<!-- prettier-ignore -->
```ts
() => FormApi
```
The `useForm()` hook plucks the [`FormApi`](/docs/final-form/types/FormApi) out of the React context for you. It will throw an exception if you try to use it outside of a [`<Form/>`](Form) component.
`useForm()` is used internally inside [`useField()`](useField), [`<Field/>`](Field), and [`<FormSpy/>`](FormSpy).
================================================
FILE: docs/api/useFormState.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/useFormState). Links may not work on Github.com.
# `useFormState`
```ts
import { useFormState } from 'react-final-form'
```
The `useFormState()` hook takes one optional parameter, which matches the exact shape of [`FormSpyProps`](../types/FormSpyProps) (except without the render props). It returns a [`FormState`](/docs/final-form/types/FormState).
`useFormState()` is used internally inside [`<FormSpy/>`](FormSpy).
================================================
FILE: docs/api.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api). Links may not work on Github.com.
# API
The API for React Final Form consists of three components and three hooks:
## Components
### [`<Form/>`](api/Form)
A component that surrounds your entire form and manages the form state. It can inject form state and functionality, e.g. a `handleSubmit` function for you to pass to your `<form>` element, via render props.
### [`<Field/>`](api/Field)
A component that lives inside your `<Form/>` and creates a "field". It register itself with the surrounding `<Form/>` tag and manages all the state for a particular field, providing input callbacks (e.g. `onBlur`, `onChange`, and `onFocus`) as well as the value of the form and myriad metadata about the state of the field.
### [`<FormSpy/>`](api/FormSpy)
_[Advanced Usage]_ A component that can tap into form-wide state from inside your `<Form/>`. It's primarily only for advanced usage when form renders are being restricted via the `subscription` prop.
## Hooks
### [`useField()`](api/useField)
A hook that will convert any of your components into a `<Field/>` component, registering with the surrounding `<Form/>` and providing field state to your component. `useField()` is used internally by `<Field/>`.
### [`useForm()`](api/useForm)
A hook that will pluck the Final Form [`form` instance](/docs/final-form/types/FormApi) out of context.
### [`useFormState()`](api/useFormState)
A hook that will convert any of your components into a `<FormSpy/>` component, allowing fine-grained control over subscribing to parts of the form state.
================================================
FILE: docs/examples/chakra.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/chakra). Links may not work on Github.com.
# Chakra UI Example
Demonstrates how to use [Chakra UI](https://chakra-ui.com) components with React Final Form.
[](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/chakra)
================================================
FILE: docs/examples/field-level-validation.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/field-level-validation). Links may not work on Github.com.
# Field Level Validation Example
Introduces field-level validation functions and demonstrates how to display errors next to fields using child render functions.
[](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/field-level-validation)
================================================
FILE: docs/examples/record-level-validation.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/record-level-validation). Links may not work on Github.com.
# Record-Level Example
Introduces a whole-record validation function and demonstrates how to display errors next to fields using child render functions.
[](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/record-level-validation)
================================================
FILE: docs/examples/simple.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/simple). Links may not work on Github.com.
# Simple Example
Uses the built-in React inputs: input, select, and textarea to build a form with no validation.
[](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/simple)
================================================
FILE: docs/examples/submission-errors.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/submission-errors). Links may not work on Github.com.
# Submission Errors
Demonstrates how to return submission errors from failed submits. Notice that the `Promise` should _resolve_ to the submission error (not reject). Rejection is reserved for communications or server exceptions.
[](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/submission-errors)
================================================
FILE: docs/examples/subscriptions.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/subscriptions). Links may not work on Github.com.
# High Performance Through Subscriptions Example
Demonstrates how, by restricting which parts of form state the form component needs to render, it reduces the number of times the whole form has to rerender. Yet, if some part of form state is needed inside of it, the [`<FormSpy/>`](../api/FormSpy) component can be used to attain it.
[](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/subscriptions)
================================================
FILE: docs/examples/wizard.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/wizard). Links may not work on Github.com.
# Wizard Form Example
Demonstrates how to use React Final Form to create a multi-page "wizard" form, with validation on each page.
[](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/wizard)
================================================
FILE: docs/examples.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples). Links may not work on Github.com.
# Examples
---
Wanna help? We need to migrate all of these examples from CodeSandbox to [here](https://github.com/final-form/react-final-form/tree/master/examples). PRs to help with that process would be greatly appreciated. 🙏
---
### [Simple Example](examples/simple)
Uses the built-in React inputs: `input`, `select`, and `textarea` to build a form with no validation.
### [Synchronous Record-Level Validation](examples/record-level-validation)
Introduces a whole-record validation function and demonstrates how to display errors next to fields using child render functions.
### [Synchronous Field-Level Validation](examples/field-level-validation)
Introduces field-level validation functions and demonstrates how to display errors next to fields using child render functions.
### [Synchronous Record-Level Validation (with delayed error render)](https://codesandbox.io/s/z2zqr008pm)
Sometimes you want to give your user a chance to make it through a brief invalid value on their way to a valid one, e.g. a date string that needs two numbers on either side of a slash. With a simple delayed rendering component, this becomes easy. Plus, the error will disappear immediately when the user fixes the problem.
### [Asynchronous Field-Level Validation](https://codesandbox.io/s/wy7z7q5zx5)
Demonstrates how field-level validation rules may be asynchronous (return a
`Promise`), as well as how to show a "validating" spinner during the lifetime of
the `Promise`.
### [Hybrid Synchronous/Asynchronous Record-Level Validation](https://codesandbox.io/s/kl9n295n5)
Demonstrates how you can mix synchronous and asynchronous validation patterns at
the record-level, by returning errors synchronously, and falling back to an
asynchronous call (by returning a `Promise`) if sync validation is passing.
### [Submission Errors](examples/submission-errors)
Demonstrates how to return submission errors from failed submits. Notice that the `Promise` should _resolve_ to the submission error (not reject). Rejection is reserved for communications or server exceptions.
### [Third Party Components](https://codesandbox.io/s/40mr0v2r87)
Demonstrates how easy it is to use third party input components. All the third
party component really needs is `value` and `onChange`, but more complex
components can accept things like errors.
### Material-UI
- [Wrapper components](https://github.com/lookfirst/mui-rff) / [Codesandbox demo](https://codesandbox.io/s/react-final-form-material-ui-example-tqv09)
### 💥 [Performance Optimization Through Subscriptions](examples/subscriptions) 💥
Demonstrates how, by restricting which parts of form state the form component needs to render, it reduces the number of times the whole form has to rerender. Yet, if some part of form state is needed inside of it, the [`<FormSpy/>`](api/FormSpy) component can be used to attain it.
### [Strongly Typed Form and Field Values with TypeScript](https://codesandbox.io/s/strongly-typed-form-values-with-react-final-form-26jkd)
Demonstrates how to use JSX generics to strongly type fields, forcing only a component that can accept the type for that field.
### [Independent Error Component (with Render Props)](https://codesandbox.io/s/xoo3xq654p)
Demonstrates how to make an independent Error component to subscribe to and
display the error for any form field.
### [Independent Error Component (with Hooks)](https://codesandbox.io/s/react-final-form-independent-error-component-with-hooks-y1grn)
Demonstrates how to make an independent Error component, using Hooks, to subscribe to and
display the error for any form field.
### [Loading and Initializing Values](https://codesandbox.io/s/91w9ro3x9o)
Demonstrates how a form can be initialized, after fetching data, by passing in
`initialValues` as a prop.
### [Field Arrays](https://codesandbox.io/s/kx8qv67nk5)
Demostrates how to use the `<FieldArray/>` component, from
[`react-final-form-arrays`](https://github.com/final-form/react-final-form-arrays),
to render an array of inputs, as well as use `push`, `pop`, and `remove`
mutations.
### [Fields Component](https://codesandbox.io/s/pyrwplknom)
Wondering how to get field state from multiple fields at once?
People coming from Redux-Form might be wondering where the equivalent of Redux Form's `Fields` component is, as a way to get state from several fields at once. The answer is that it's not included in the library because it's so easy to write one recursively composing `Field` components together.
### [Calculated Fields](https://codesandbox.io/s/oq52p6v96y)
Demonstrates how to use the
[`final-form-calculate`](https://github.com/final-form/final-form-calculate)
decorator to achieve realtime field calculations through easily defined rules.
### [Field Warnings](https://codesandbox.io/s/m5qwxpr6o8)
Demonstrates how the power of subscriptions and mutators can be used to build a
warning engine: logic to display a message next to each field that is _not_ an
error (thus it does _not_ prevent form submission).
### [Reusable Field Groups](https://codesandbox.io/s/8z5jm6x80)
Demonstrates how fields can be grouped into reusable components.
### [Prefixed Fields](https://codesandbox.io/s/react-final-form-prefixed-fields-seiy8)
Demonstrates how the React context API can be used to provide a "prefix wrapper"
around fields to add structure to your form date. It's similar to how Redux Form's
[`FormSection`](https://redux-form.com/8.2.2/docs/api/formsection.md/) component works.
Between this and the [Reusable Field Groups](#reusable-field-groups) example, your
use case, if migrating from `FormSection` should be handled.
### [External Submit](https://codesandbox.io/s/1y7noyrlmq)
Demonstrates how you can use `document.getElementById()` or a closure to trigger
a submit from outside of the form. For more information, see
[How can I trigger a submit from outside the form?](https://final-form.org/docs/react-final-form/faq#how-can-i-trigger-a-submit-from-outside-my-form)
### [Wizard Form](examples/wizard)
Demonstrates how to use React Final Form to create a multi-page "wizard" form, with validation on each page.
### [Parse and Format (and Normalize)](https://codesandbox.io/s/10rzowm323)
Demonstrates how to use 🏁 React Final Form's `parse` and `format` props to control exactly how the data flows from the form state through the input and back to the form state. Notice that you can use `parse` to "normalize" your values.
### [Auto-Save with Debounce](https://codesandbox.io/s/5w4yrpyo7k)
Demonstrates how to use a `FormSpy` component to listen for value changes and automatically submit different values after a debounce period.
### [Auto-Save with Selective Debounce](https://codesandbox.io/s/98j0v46zj4)
Demonstrates how to use a `FormSpy` component to listen for value changes and automatically submit different values after a debounce period, but only does the debounce for certain specified fields, in this case, all the text fields.
### [Auto-Save on Field Blur](https://codesandbox.io/s/7k742qpo36)
Demonstrates how to use a `FormSpy` component to listen for values and active field changes to automatically submit values when fields are blurred.
### [Custom Validation Engine](https://codesandbox.io/s/kxxw4l0p9o)
Demonstrates how incredibly extensible `FormSpy`, the [`setFieldData` mutator](https://github.com/final-form/final-form-set-field-data), and render props are by implementing a custom validation engine completely apart from the built-in validation in 🏁 Final Form, thus allowing for special behaviors, like only validating a single field when that field is blurred.
### [Loading, Normalizing, Saving, and Reinitializing](https://codesandbox.io/s/xr0mvl1904)
Demonstrates how to make a wrapper component to handle loading, normalization of data, saving, and reinitializing of the form, to maintain `pristine`/`dirty` state with saved data.
### [🏎️ Downshift Type-Ahead](https://codesandbox.io/s/qzm43nn2mj)
Demonstrates how to use a [🏎️ Downshift](https://github.com/paypal/downshift) type-ahead component as an input.
### [Redux Example](https://codesandbox.io/s/4xq2qpzw79)
The only reason to keep your 🏁 Final Form form data in Redux is if you need to be able to read it from outside your form. This example demonstrates how to use a `FormSpy` to keep a copy of your form data in the Redux store. Note that the canonical authoritative version of the data still lives in 🏁 Final Form. If you need to _mutate_ your data via dispatching Redux actions, you should probably use [Redux Form](https://redux-form.com).
### [Conditional Fields](https://codesandbox.io/s/lm4p3m92q)
Sometimes you might want to conditionally show or hide some parts of your form depending on values the user has already provided for other form inputs. 🏁 React Final Form makes that very easy to do by creating a `Condition` component out of a `Field` component.
### [Listening for External Changes](https://codesandbox.io/s/3x989zl866)
By wrapping a stateful `ExternalModificationDetector` component in a `Field` component, we can listen for changes to a field's value, and by knowing whether or not the field is active, deduce when a field's value changes due to external influences.
### [Focus On First Error](https://codesandbox.io/s/6174kqr403)
Demonstrates how to incorporate the [🏁 Final Form Focus 🧐](https://github.com/final-form/final-form-focus) decorator to provide this functionality out of the box.
### [Credit Card Example](https://codesandbox.io/s/9y8vkrrx9o)
Demonstrates how to make an awesome credit card UX using [React Credit Cards](https://github.com/amarofashion/react-credit-cards).
### [Async Redux Submission](https://codesandbox.io/s/x71mx66z8w)
Want to use `redux-saga` or `redux-observable` to manage your form submissions? Now you can, using [`react-redux-promise-listener`](https://github.com/erikras/react-redux-promise-listener#react-redux-promise-listener) to convert your dispatched Redux actions into the `Promise` that 🏁 React Final Form is expecting for its `onSubmit` function.
### [Declarative Form Rules](https://codesandbox.io/s/52q597j2p)
What if you could define rules to update fields when other fields change _as components_? This example explores such possibilities. There's also [a Medium post](https://medium.com/@erikras/declarative-form-rules-c5949ea97366) about writing it, and creating a companion library, [`react-final-form-listeners`](https://github.com/final-form/react-final-form-listeners#-react-final-form-listeners).
### [Format String By Pattern](https://codesandbox.io/s/no20p7z3l)
Demonstrates how to use the library `format-string-by-pattern` to create input masks for your 🏁 React Final Form fields.
### [AsyncTypeahead and Redux](https://codesandbox.io/s/5m4w2909k)
Demonstrates creating an `AsyncTypeahead` to select github users, while storing the search results in the redux store and the form state (selected github users) via `react-final-form`. Also makes use of the [`setFieldData` mutator](https://github.com/final-form/final-form-set-field-data).
### [Format On Blur](https://codesandbox.io/s/3rp260ly51)
Demonstrates how to use the `formatOnBlur` prop to postpone the formatting of a form field value until the field loses focus. Very useful for formatting numbers, like currencies.
### [Styling with 🍭 Smooth-UI](https://codesandbox.io/s/40o45po3l4)
Demonstrates how to use the Smooth-UI styling library to make your forms look fabulous! All you really need is a higher order component that adapts The 🍭 Smooth-UI form controls to work with 🏁 React Final Form.
### [Styling with Chakra-UI](examples/chakra)
Demonstrates how to use the [Chakra UI](https://chakra-ui.com) styling library to make your forms look fabulous!
### [CLI Example](https://github.com/final-form/rff-cli-example) 🤯
Yes! You can actually use 🏁 React Final Form in a command line interface! Thanks to packages like [Ink](https://github.com/vadimdemedes/ink) and [Pastel](https://github.com/vadimdemedes/pastel), the power of 🏁 Final Form's form state management works just fine on the command line.
================================================
FILE: docs/faq.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/faq). Links may not work on Github.com.
# FAQ
Below are some frequently asked questions.
## Why not Redux-Form or Formik?
Those are both excellent form libraries. Like all engineering decisions, it depends on your requirements and what trade-offs you wish to make. Both Redux-Form and Formik have considerably larger bundle sizes. Compare [Formik](https://bundlephobia.com/result?p=formik) and [Redux Form](https://bundlephobia.com/result?p=redux-form) to [Final Form](https://bundlephobia.com/result?p=final-form) + [React Final Form](https://bundlephobia.com/result?p=react-final-form).
Redux-Form and React Final Form were both written by [@erikras](https://twitter.com/erikras), who recommends that, unless you _really_ need your form data intimately tied to Redux, you should start any new projects with React Final Form, and try to migrate any older Redux Form projects to it as well.
## Why no HOC?
The only benefit that higher order components provide over render props is access to the injected props from within component lifecycle methods. Plus, it only takes a single line of code to transform a component with a `render` (or `component`) prop into a HOC. If you really want a HOC, you can write your own:
```jsx
import { Form, Field } from 'react-final-form'
class MyForm extends React.Component {
componentDidMount() {
const { initialize } = this.props // access to injected props
ajax.fetch('/myData').then(data => initialize(data))
}
render() {
return <form onSubmit={this.props.handleSubmit}>...some fields...</form>
}
}
// 👇 THIS LINE IS THE HOC 👇
export default props => <Form {...props} component={MyForm} />
```
Doing a HOC
[properly](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/withRouter.js),
as a library should, with hoisted statics and `displayName` and `ref`, etc., is
a hassle and would add unnecessary bulk.
## How can I trigger a submit from outside my form?
This is a common question I see from people migrating from `redux-form`. There
are three possible solutions:
### Via the `form` attribute
You can provide the form id to a submit button.
```jsx
<button type="submit" form="myForm">Submit</button>
{/* ^^^^^^^^^^^^^ */}
<form id="myForm" onSubmit={handleSubmit}>
{/* ^^^^^^^^^^^ */}
...fields go here...
</form>
```
### Via `document.getElementById()`
You can use the DOM to get a reference to your `<form>` element and dispatch a
submit event on it. Note that you cannot just call `submit()`, as this will not
trigger React's event handlers.
```jsx
<button onClick={() => {
document.getElementById('myForm').submit() // ❌
}}>Submit</button>
<button onClick={() => {
document.getElementById('myForm')
.dispatchEvent(new Event('submit', { cancelable: true, bubbles:true })) // ✅
}}>Submit</button>
<form id="myForm" onSubmit={handleSubmit}>
...fields go here...
</form>
```
See [Sandbox Example](https://codesandbox.io/s/1y7noyrlmq).
### Via Closure
If you define a variable outside of your form, you can then set the value of
that variable to the `handleSubmit` function that 🏁 React Final Form gives you,
and then you can call that function from outside of the form.
```jsx
let submit
return (
<div>
<button onClick={submit}>Submit</button> // ❌ Not overwritten closure value
<button onClick={event => submit(event)}>Submit</button> // ✅
<Form
onSubmit={onSubmit}
render={({ handleSubmit }) => {
submit = handleSubmit
return <form>...fields go here...</form>
}}
/>
</div>
)
```
See [Sandbox Example](https://codesandbox.io/s/1y7noyrlmq).
### Via Redux Dead Drop
If you're already using Redux, you could potentially use the same mechanism that
`redux-form` uses:
[Redux Dead Drop](https://medium.com/@erikras/redux-dead-drop-1b9573705bec).
## Why can't I have numeric keys in an object?
So you want to have value structured like `{ 13: 'Bad Luck', 42: 'Meaning of Everything' }`, but you're getting an error. This is because the `setIn` engine in 🏁 Final Form uses the `isNaN`-ness of keys to determine whether or not it should create an object or an array when constructing deep data structures. Adding checks for `bracket[3]` syntax as opposed to `dot.3` syntax adds a _lot_ of complexity, and has consciously been avoided.
You will need to convert all your keys to `NaN` strings before initializing your form values, and then convert them back to numbers on submit. It's not that hard.
```jsx
const stringifyKeys = values =>
Object.keys(values).reduce((result, key) => {
result[`key${key}`] = values[key]
return result
}, {})
const destringifyKeys = values =>
Object.keys(values).reduce((result, key) => {
result[Number(key.substring(3))] = values[key]
return result
}, {})
<Form
onSubmit={values => onSubmit(destringifyKeys(values))}
initialValues={stringifyKeys(initialValues)}>
...
</Form>
```
## I'm changing form state on first render, e.g. with FormSpy, why are my changes not reflected?
As of `v5`, the only changes that occur during the first render that will cause the form to rerender are if adding field-level validation functions changes the validity of the form. You will need to put your side effect into either a React `useEffect` hook, or just call it in a `setTimeout` to allow the form to finish rendering and setting up its side effects before you go altering the state.
================================================
FILE: docs/getting-started.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/getting-started). Links may not work on Github.com.
# Getting Started
Before we jump right into code, you might want to learn a little bit about the [philosophy](philosophy) and origin story of React Final Form.
## Installation
```bash
npm install --save final-form react-final-form
```
or
```bash
yarn add final-form react-final-form
```
## Architecture
React Final Form is a thin React wrapper for [Final Form](/), which is a subscriptions-based form state management library that uses the [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern), so only the components that need updating are re-rendered as the form's state changes.
By default, **React Final Form subscribes to _all_ changes**, but if you want to fine tune your form to optimized blazing-fast perfection, you may specify only the form state that you care about for rendering your gorgeous UI. You can think of it a little like GraphQL's feature of only fetching the data your component needs to render, and nothing else.
## Code
Here's what it looks like in your code:
```jsx
import { Form, Field } from 'react-final-form'
const MyForm = () => (
<Form
onSubmit={onSubmit}
validate={validate}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<h2>Simple Default Input</h2>
<div>
<label>First Name</label>
<Field name="firstName" component="input" placeholder="First Name" />
</div>
<h2>An Arbitrary Reusable Input Component</h2>
<div>
<label>Interests</label>
<Field name="interests" component={InterestPicker} />
</div>
<h2>Render Function</h2>
<Field
name="bio"
render={({ input, meta }) => (
<div>
<label>Bio</label>
<textarea {...input} />
{meta.touched && meta.error && <span>{meta.error}</span>}
</div>
)}
/>
<h2>Render Function as Children</h2>
<Field name="phone">
{({ input, meta }) => (
<div>
<label>Phone</label>
<input type="text" {...input} placeholder="Phone" />
{meta.touched && meta.error && <span>{meta.error}</span>}
</div>
)}
</Field>
<button type="submit">Submit</button>
</form>
)}
/>
)
```
[Let's explore the API...](api)
================================================
FILE: docs/migration/formik.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/migration-guide/formik). Links may not work on Github.com.
# Migration from Formik
Good news! Because both React Final Form and Formik are second generation form libraries, they both copied much of their API from [Redux Form](https://redux-form.com), so, despite working very differently under the hood, there is a lot of overlap in their APIs.
## Props passed to `<Formik/>`
The following props passed to `<Formik/>` are more or less identical to those you can pass to [`<Form/>`](../api/Form):
- [`onSubmit`](../types/FormProps#onsubmit)
- [`initialValues`](../types/FormProps#initialvalues)
- [`validate`](../types/FormProps#validate)
The render props work the same way, except for [what is passed](#props-passed-by-formik).
- [`children`](../types/FormProps#children)
- [`component`](../types/FormProps#component)
- [`render`](../types/FormProps#render)
The only tricky one is:
- [`validateOnBlur`](../types/FormProps#validateonblur)
It's not the same at all. In Formik, `validateOnBlur` defaults to `true` and it allows you to tell Formik _not_ to validate on blur. React Final Form validates on every change by default, and setting `validateOnBlur` to `true` is a way to tell React Final Form to _only_ validate on blur (to _not_ validate on change).
## Props passed by `<Formik/>`
The following props passed into the render functions by `<Formik/>` are identical:
- [`dirty`](/docs/final-form/types/FormState#dirty)
- [`errors`](/docs/final-form/types/FormState#errors)
- [`touched`](/docs/final-form/types/FormState#touched)
- [`handleSubmit`](/docs/final-form/types/FormRenderProps#handlesubmit)
To migrate from Formik, you'd have to search and replace the following:
| Formik | React Final Form |
| ------------------- | ----------------------------------------------------------- |
| `isSubmitting` | [`submitting`](/docs/final-form/types/FormState#submitting) |
| `isValid` | [`valid`](/docs/final-form/types/FormState#valid) |
| `isValidating` | [`validating`](/docs/final-form/types/FormState#validating) |
| `resetForm()` | [`form.reset()`](/docs/final-form/types/FormApi#reset) |
| `submitForm()` | [`form.submit()`](/docs/final-form/types/FormApi#submit) |
| `setFieldTouched()` | [`form.blur()`](/docs/final-form/types/FormApi#blur) |
| `setFieldValue()` | [`form.change()`](/docs/final-form/types/FormApi#change) |
## Props passed to `<Field/>`
The APIs of the [`<Field/>`](../api/Field) components are very similiar. They both have:
- [`name`](../types/FieldProps#name)
- [`children`](../types/FieldProps#children)
- [`component`](../types/FieldProps#component)
- [`render`](../types/FieldProps#render)
- [`validate`](../types/FieldProps#validate)
One difference is that Formik's `<Field/>` will default to `component="input"` if no rendering strategy is provided. React Final Form requires you to specify one of the render strategies.
## Props passed by `<Field/>`
The biggest difference is that Formik puts all the `name`, `onChange`, `onBlur`, and `value` into a prop called `field`, React Final Form puts them into a prop called `input`. So:
Formik:
```jsx
<Field
name="lastName"
render={({ field }) => <input {...field} placeholder="Last Name" />}
/>
```
React Final Form:
```jsx
<Field
name="lastName"
render={({ input }) => <input {...input} placeholder="Last Name" />}
// ^^^^^ ^^^^^
/>
```
The other difference is that, while Formik just gives your field component the Formik instance to query for information about field state, React Final Form provides your field state for you in the `meta` prop. Things like [`active`](../types/FieldRenderProps#metaactive) (which Formik doesn't even track), [`dirty`](../types/FieldRenderProps#metadirty), [`pristine`](../types/FieldRenderProps#metapristine), [`valid`](../types/FieldRenderProps#metavalid), [`invalid`](../types/FieldRenderProps#metainvalid), [`touched`](../types/FieldRenderProps#metatouched), [`validating`](../types/FieldRenderProps#metavalidating), and [`visited`](../types/FieldRenderProps#metavisited) (also not in Formik).
## Formik's `<Form/>`
React Final Form doesn't provide a similar component, because it just doesn't seem that useful, and it's also trivial to write one yourself.
```jsx
import { useForm } from 'react-final-form'
const FormLikeFormik = props => {
const form = useForm()
return (
<form
{...props}
onSubmit={event => {
event.preventDefault()
form.submit()
}}
/>
)
}
```
## Formik's `<ErrorMessage/>`
This component _does_ seem useful, and writing your own is a great introduction to React Final Form's subscription system.
### With `<Field/>`
```jsx
import { Field } from 'react-final-form'
const ErrorMessage = ({ name }) => (
<Field name={name} subscription={{ error: true, touched: true }}>
{({ meta: { error, touched } }) =>
error && touched ? <span>{error}</span> : null
}
</Field>
)
```
...or, if you want to look cool...
### With Hooks
```jsx
import { useField } from 'react-final-form'
const ErrorMessage = ({ name }) => {
const {
meta: { error, touched }
} = useField(name, { subscription: { error: true, touched: true } })
return error && touched ? <span>{error}</span> : null
}
```
## Formik's `<FastField/>`
React Final Form's [`<Field/>`](../api/Field) has always, from day one, avoided rerenders when parts of the form state change that don't affect the field in question. On top of that, React Final Form's `<Field/>` provides a [`subscription`](../types/FieldProps#subscription) prop that allows you to have even more fine-grain control over _precisely_ which form state will cause your field to rerender.
## Formik's `connect()`
React Final Form has no higher order components, because [it's just not necessary](../faq#why-no-hoc). If you need access to the Final Form instance, you can use the [`useForm()`](../api/useForm) hook.
## A Diff
This is what would need to change to migrate the form on Formik's [Overview](https://jaredpalmer.com/formik/docs/overview#reducing-boilerplate) docs page to React Final Form.
```diff
import React from 'react'
-import { Formik, Form, Field, ErrorMessage } from 'formik'
+import { Form, Field, useField } from 'react-final-form'
+// Obviously this could be reused across your project
+const ErrorMessage = ({ name, component }) => {
+ const {
+ meta: { error, touched }
+ } = useField(name, { subscription: { error: true, touched: true } })
+ return error && touched
+ ? React.createElement(component, null, error)
+ : null
+}
const Basic = () => (
<div>
<h1>Any place in your app!</h1>
- <Formik
+ <Form
initialValues={{ email: '', password: '' }}
validate={values => {
let errors = {}
if (!values.email) {
errors.email = 'Required'
} else if (
!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Invalid email address'
}
return errors
}}
- onSubmit={(values, { setSubmitting }) => {
+ onSubmit={(values) => {
+ // final-form manages submitting status
+ // for you *if* you return a promise
+ return new Promise(resolve =>
setTimeout(() => {
alert(JSON.stringify(values, null, 2))
- setSubmitting(false)
+ resolve()
}, 400)
+ )
}}
>
- {({ isSubmitting }) => (
- <Form>
+ {({ handleSubmit, submitting }) => (
+ <form onSubmit={handleSubmit}>
- <Field type="email" name="email" />
+ <Field type="email" name="email" component="input"/>
<ErrorMessage name="email" component="div" />
- <Field type="password" name="password" />
+ <Field type="password" name="password" component="input"/>
<ErrorMessage name="password" component="div" />
- <button type="submit" disabled={isSubmitting}>
+ <button type="submit" disabled={submitting}>
Submit
</button>
- </Form>
+ </form>
)}
- </Formik>
+ </Form>
</div>
)
export default Basic
```
================================================
FILE: docs/migration/redux-form.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/migration-guide/redux-form). Links may not work on Github.com.
# Migration from Redux Form
Good news! React Final Form was written by the same guy ([@erikras](https://twitter.com/erikras)) that wrote Redux Form, so much of the API is exactly the same. The primary difference is that, rather than "decorate" your form component with a Higher Order Component, you use React Final Form's [`<Form/>`](../api/Form) component to give you all your form state via a render prop. Most of the `config` properties from Redux Form maps directly onto the props to [`<Form/>`](../api/Form), e.g. [`initialValues`](../types/FormProps#initialvalues), [`onSubmit`](../types/FormProps#onsubmit), [`validate`](../types/FormProps#validate), etc.
### Step 1: Change your imports
```diff
import React from 'react'
-import { reduxForm, Field } from 'redux-form'
+import { Form, Field } from 'react-final-form'
```
### Step 2: Surround your `<form/>` with a `<Form/>`
```diff
import React from 'react'
import { Form, Field } from 'react-final-form'
const MyForm = props => {
const { handleSubmit, pristine, reset, submitting } = props
return (
+ <Form>
+ {() => (
<form onSubmit={handleSubmit}>
...fields here...
</form>
+ )}
+ </Form>
)
}
```
### Step 3: Move all your `config` values from `reduxForm()` to be props of `<Form/>`
```diff
import React from 'react'
import { Form, Field } from 'react-final-form'
const MyForm = props => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<Form
+ initialValues={{
+ firstName: 'Dan'
+ }}
+ onSubmit={values => {
+ // send values to the cloud
+ }}
+ validate={values => {
+ // do validation here, and return errors object
+ }}
>
{() => (
<form onSubmit={handleSubmit}>
...fields here...
</form>
)}
</Form>
)
}
export default reduxForm({
form: 'myForm',
- initialValues: {
- firstName: 'Dan'
- },
- onSubmit: values => {
- // send values to the cloud
- },
- validate: values => {
- // do validation here, and return errors object
- }
})(MyForm)
```
### Step 4: Get form state from `<Form/>`, not as props
```diff
import React from 'react'
import { Form, Field } from 'react-final-form'
const MyForm = props => {
- const { handleSubmit, pristine, reset, submitting } = props
return (
<Form
initialValues={{
firstName: 'Dan'
}}
onSubmit={values => {
// send values to the cloud
}}
validate={values => {
// do validation here, and return errors object
}}
>
+ {({ handleSubmit, pristine, reset, submitting }) => (
<form onSubmit={handleSubmit}>
...fields here...
</form>
)}
</Form>
)
}
export default reduxForm({
form: 'myForm'
})(MyForm)
```
### Step 5: Observe API changes
_Some_ of the API is slightly different. For example, rather than providing a `reset` function, the entire [`FormApi`](/docs/final-form/types/FormApi) object is provided, on which `reset()` is a function.
```diff
import React from 'react'
import { Form, Field } from 'react-final-form'
const MyForm = props => {
return (
<Form
initialValues={{
firstName: 'Dan'
}}
onSubmit={values => {
// send values to the cloud
}}
validate={values => {
// do validation here, and return errors object
}}
>
- {({ handleSubmit, pristine, reset, submitting }) => (
+ {({ handleSubmit, pristine, form, submitting }) => (
<form onSubmit={handleSubmit}>
...fields here...
<div>
<button type="submit" disabled={submitting}>
Submit
</button>
<button
type="button"
disabled={pristine || submitting}
- onClick={reset}
+ onClick={form.reset}
>
Clear Values
</button>
</div>
</form>
)}
</Form>
)
}
export default reduxForm({
form: 'myForm'
})(MyForm)
```
### Step 6: Remove the HOC
Now you can just export your component. No HOC decorator needed! [Why?](../faq#why-no-hoc)
```diff
-export default reduxForm({
- form: 'myForm'
-})(MyForm)
+export default MyForm
```
### Step 7: Error management
No more `SubmissionError` to throw, error management is made with the return value.
Have a look here to know more: https://final-form.org/docs/react-final-form/types/FormProps#onsubmit
### Step 8: Define your `renderField` functions inline (if you want)
With Redux Form, it was common to have to do something like this:
```jsx
// outside your render() method
const renderField = (field) => (
<div className="input-row">
<input {...field.input} type="text"/>
{field.meta.touched && field.meta.error &&
<span className="error">{field.meta.error}</span>}
</div>
)
// inside your render() method
<Field name="myField" component={renderField}/>
```
With React Final Form, you can define your render logic directly your JSX.
```jsx
// inside your render() method
<Field name="myField">
{field => (
<div className="input-row">
<input {...field.input} type="text" />
{field.meta.touched && field.meta.error && (
<span className="error">{field.meta.error}</span>
)}
</div>
)}
</Field>
```
It's a little easier to read, but it's less reusable. Large projects are going to create reusable field render functions. If you're migrating a mature project, you can probably continue to use your existing functions/components. [`<Field/>`](../api/Field) provides the same [`{ input, meta }` shape](../types/FieldRenderProps) that Redux Form does.
### The Full Diff
```diff
import React from 'react'
-import { reduxForm, Field } from 'redux-form',
+import { Form, Field } from 'react-final-form'
const MyForm = props => {
- const { handleSubmit, pristine, reset, submitting } = props
return (
+ <Form
+ initialValues={{
+ firstName: 'Dan'
+ }}
+ onSubmit={values => {
+ // send values to the cloud
+ }}
+ validate={values => {
+ // do validation here, and return errors object
+ }}
+ >
+ {({ handleSubmit, pristine, form, submitting }) => (
<form onSubmit={handleSubmit}>
<div>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
/>
</div>
<div>
<label>Last Name</label>
<Field
name="lastName"
component="input"
type="text"
placeholder="Last Name"
/>
</div>
<div>
<button type="submit" disabled={submitting}>
Submit
</button>
<button
type="button"
disabled={pristine || submitting}
- onClick={reset}
+ onClick={form.reset}
>
Clear Values
</button>
</div>
</form>
+ )}
+ </Form>
)
}
-export default reduxForm({
- form: 'myForm',
- initialValues: {
- firstName: 'Dan'
- },
- onSubmit: values => {
- // send values to the cloud
- },
- validate: values => {
- // do validation here, and return errors object
- }
-})(MyForm)
+export default MyForm
```
### `<Fields/>`
If you're accustomed to being able to get the state of many fields at once using Redux Form's `<Fields/>`, you may be wondering why this library does not include it. The answer is that _most_ people don't need it, but if you do, you can write it yourself recursively, like this:
```tsx
const Fields = ({
names,
subscription,
fieldsState = {},
children,
originalRender
}) => {
if (!names.length) {
return (originalRender || children)(fieldsState)
}
const [name, ...rest] = names
return (
<Field name={name} subscription={subscription}>
{fieldState => (
<Fields
names={rest}
subscription={subscription}
originalRender={originalRender || children}
fieldsState={{ ...fieldsState, [name]: fieldState }}
/>
)}
</Field>
)
}
```
Here's a sandbox demonstrating its usage:
[](https://codesandbox.io/s/pyrwplknom?fontsize=14)
### `<FieldArray/>`
React Final Form does not come with field arrays right out of the box. This is because many projects do not need them, and the [philosophy](../philosophy#modularity) of React Final Form is to keep bundle size small, but provide ways to add additional functionality when you need it.
You will need to install two additional packages, `final-form-arrays`, which provides array functionality to the core Final Form instance, and `react-final-form-arrays`, which contains the `<FieldArray/>` component. If you are accustomed to using Redux Form's `<FieldArray/>` component, you already know the API for React Final Form's `<FieldArray/>` component. It injects an "array-like" object called `fields`, which you can `map()` over to get the `string` names for each of the fields in the array. You pass these field names to the `<Field/>` component to render one of the fields in your array. It works just like in Redux Form.
```tsx
import arrayMutators from 'final-form-arrays'
import { FieldArray } from 'react-final-form-arrays'
const MyForm = () => (
<Form
onSubmit={onSubmit}
mutators={{ ...arrayMutators }}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ IMPORTANT!
>
{({
handleSubmit,
form: {
mutators: { push, pop } // injected from final-form-arrays above
}
}) => (
<form onSubmit={handleSubmit}>
... other fields here maybe ...
<button type="button" onClick={() => push('customers', undefined)}>
Add Customer
</button>
<button type="button" onClick={() => pop('customers')}>
Remove Customer
</button>
<FieldArray name="customers">
{({ fields }) =>
fields.map((name, index) => (
<div key={name}>
<label>Cust. #{index + 1}</label>
<Field
name={`${name}.firstName`}
component="input"
placeholder="First Name"
/>
</div>
))
}
</FieldArray>
</form>
)}
</Form>
)
```
Here's a sandbox to demonstrate:
[](https://codesandbox.io/s/distracted-bhaskara-kx8qv67nk5?fontsize=14)
### Cheat Code: The Lazy Way
⚠️ NOT RECOMMENDED ⚠️
There's some chance that you could get away with implementing this function that replicates what the `reduxForm()` decorator does:
```jsx
import { Form } from 'react-final-form'
const reactFinalForm = ({ form, ...config }) => component => props => (
<Form {...config} {...props} component={component} />
)
```
Then you'd only have to change that one thing.
```diff
-export default reduxForm({
+export default reactFinalForm({
form: 'myForm',
initialValues: {
firstName: 'Dan'
},
onSubmit: values => {
// send values to the cloud
},
validate: values => {
// do validation here, and return errors object
}
})(MyForm)
```
But cheats like this create technical debt, so do the right thing and refactor your forms to use render props. 😄
================================================
FILE: docs/philosophy.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/philosophy). Links may not work on Github.com.
# Philosophy
For several years, I ([@erikras](https://twitter.com/erikras)) actively maintained the first big form library in the React community, [Redux Form](https://redux-form.com). During those years, I learned many lessons, about open source and React, and saw hundreds of forms use cases from around the world. As Redux Form grew in popularity (and bundle size), I received a lot of feedback from the community. React Final Form is my answer to the concerns of the community.
## Talk
In this talk, I explain the journey through Redux Form to the conception and creation of React Final Form.
[Next Generation Forms with React Final Form – React Alicante 2018, Alicante, Spain](https://youtu.be/WoSzy-4mviQ)
## Goals
React Final Form strives to meet the following goals:
### Strongly Typed
React Final Form provides strong typing via both [Flow](https://flow.org) and [Typescript](https://www.typescriptlang.org) to allow you to catch common bugs _at coding time_.
### Modularity
Just because some forms can be complex doesn't mean that your users should need to download all that code for a simple form! React Final Form and Final Form break out complex functionality into separate packages, so the form state management core doesn't get bloated by complicated use cases. This allows you to _build the form library you need_ for every use case.
Also, this allows for...
### Minimal Bundle Size
React Final Form is a minimal wrapper around the _zero-dependency_ Final Form core. All React Final Form does is know how to get form values out of [`SyntheticEvent`](https://reactjs.org/docs/events.html) and manage field subscriptions to the form.
### High Performance
You probably won't need to fine-tune your form performance, but if your form grows and starts to lag, you'll be glad you've chosen React Final Form. Every bit of form and field state can be chosen _à la carte_ to trigger a rerender in React.
If you're familiar with Redux in React, it's a little bit like how you can use [selectors](https://redux.js.org/recipes/computing-derived-data) to specify exactly which "slice" of state you want your component to be notified about.
The result is that you can streamline your form for maximum performance.
[Ready to get started?](getting-started)
================================================
FILE: docs/types/FieldProps.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FieldProps). Links may not work on Github.com.
# `FieldProps`
These are props that you pass to [`<Field/>`](../api/Field). You must provide one of the ways to render: `component`, `render`, or `children`.
## `afterSubmit`
```ts
() => void
```
Optional.
A callback to notify fields after submission has completed successfully.
## `allowNull`
```ts
boolean
```
Optional. Defaults to `false`.
By default, if your value is `null`, [`<Field/>`](../api/Field) will convert it to `''`, to ensure
[controlled inputs](https://reactjs.org/docs/forms.html#controlled-components).
But if you pass `true` to `allowNull`, [`<Field/>`](..api/Field) will give you a `null` value.
## `beforeSubmit`
```ts
() => void | false
```
Optional.
A function to call just before calling `onSubmit`. If `beforeSubmit` returns `false`, the submission will be aborted. If one of your fields returns `false` on `beforeSubmit`, other fields may not have their `beforeSubmit` called, as the submission is aborted on the first one that returns `false`.
## `children`
```ts
((props: FieldRenderProps) => React.Node) | React.Node`
```
Optional. (if you specify [`component`](#component) or [`render`](#render))
A render function that is given [`FieldRenderProps`](FieldRenderProps), as well as any non-API props passed into the `<Field/>` component. For example, if you did...
```tsx
<Field name="myField" someArbitraryOtherProp={42}>
{props => {
console.log(props.someArbitraryOtherProp) // would print 42
return <input {...props.input}/>
}}
</Field>
```
Note that if you specify [`render`](#render) or [`component`](#component) _and_ `children`, `render` will be called, with `children` injected as if it were an additional prop. This can be especially useful for doing something like:
```tsx
<Field name="favoriteColor" component="select">
<option value="FF0000">Red</option>
<option value="00FF00">Green</option>
<option value="0000FF">Blue</option>
</Field>
```
Related:
- [`FieldRenderProps`](FieldRenderProps)
## `component`
```ts
React.ComponentType<FieldRenderProps> | 'input' | 'select' | 'textarea'`
```
Optional. If you are not using `'input'`, `'select`' or `'textarea'`, it is recommended that you use [`children`](#children) or [`render`](#render).
Either the `string` name of one of the default HTML inputs, or a component that is given [`FieldRenderProps`](FieldRenderProps) as props, children and render props, as well as any non-API props passed into the `<Field/>` component. For example, if you did...
<!-- prettier-ignore -->
```tsx
<Field
name="myField"
someArbitraryOtherProp={42}
component={MyFieldComp} />
const MyFieldComp = props => {
console.log(props.someArbitraryOtherProp) // would print 42
return <input {...props.input} />
}
```
Related:
- [`FieldRenderProps`](FieldRenderProps)
## `data`
```ts
Object
```
Optional.
Initial state for arbitrary values to be placed by mutators.
## `defaultValue`
```ts
any
```
Optional.
⚠️ You probably want [`initialValue`](#initialvalue)! ⚠️
The value of the field upon creation. _**This value is only needed if you want your field be `dirty` upon creation (i.e. for its value to be different from its initial value).**_
## `format`
<!-- prettier-ignore -->
```ts
(value: any, name: string) => any
```
Optional.
A function that takes the value from the form values and the name of the field and formats the value to give to the input. Common use cases include converting javascript `Date` values into a localized date string. Almost always used in conjunction with [`parse`](#parse).
**Note: If you would like to disable the default behavior of converting `undefined` to `''`, you can pass an [identity function](https://en.wikipedia.org/wiki/Identity_function), `v => v`, to `format`. If you do this, making sure your inputs are "controlled" is up to you.**
## `formatOnBlur`
```ts
boolean
```
Optional. Defaults to `false`.
If `true`, the `format` function will only be called when the field is blurred. If `false`, `format` will be called on every render.
## `initialValue`
```ts
any
```
Optional.
The initial value for the field. This value will be used to calculate `dirty` and `pristine` by comparing it to the current value of the field. If you want field to be `dirty` upon creation, you can set one value with `initialValue` and set the value of the field with `defaultValue`.
The value given here will override any `initialValues` given to the entire form.
## `isEqual`
<!-- prettier-ignore -->
```ts
(a: any, b: any) => boolean
```
Optional. Defaults to `===`.
A function to determine if two values are equal.
## `multiple`
```ts
boolean
```
Optional.
Only of use when using `component="select"` and you want a multiselect.
It will be added on your input component, or you may retrieve its value inside the "input" property of your custom components.
## `name`
```ts
string
```
**Required**
The name of your field. Field values may be deeply nested using dot-and-bracket syntax.
[Learn more about Field Names](/docs/final-form/field-names).
## `parse`
<!-- prettier-ignore -->
```ts
(value: any, name: string) => any
```
Optional.
A function that takes the value from the input and name of the field and converts the value into the value you want stored as this field's value in the form. Common usecases include converting strings into `Number`s or parsing localized dates into actual javascript `Date` objects. Almost always used in conjuction with [`format`](#format).
**Note: If would like to override the default behavior of converting `''` to `undefined`, you can pass an [identity function](https://en.wikipedia.org/wiki/Identity_function), `v => v`, to `parse`, thus allowing you to have form values of `''`.**
## `ref`
The `ref` is forwarded to the component provided by the `component` prop.
## `render`
<!-- prettier-ignore -->
```ts
(props: FieldRenderProps) => React.Node
```
Optional. (if you specify [`component`](#component) or [`children`](#children))
A render function that is given [`FieldRenderProps`](FieldRenderProps), as well as any non-API props passed into the `<Field/>` component. For example, if you did...
```tsx
<Field
name="myField"
someArbitraryOtherProp={42}
render={props => {
console.log(props.someArbitraryOtherProp) // would print 42
return <input {...props.input} />
}}
/>
```
Note that if you specify `render` _and_ [`children`](#children), `render` will be called, with `children` injected as if it were an additional prop.
Related:
- [`FieldRenderProps`](FieldRenderProps)
## `subscription`
```ts
{ [string]: boolean }
```
Optional. _Advanced Usage_
An object of the parts of [`FieldState`](/docs/final-form/types/FieldState) to subscribe to. If a subscription is provided, the [`<Field/>`](../api/Field) will only rerender when those parts of field state change.
If no `subscription` is provided, it will default to subscribing to _all_ field state changes. i.e. [`<Field/>`](../api/Field) will rerender whenever any part of the field state changes.
Related:
- [`FieldState`](/docs/final-form/types/FieldState)
## `type`
```ts
string
```
Optional.
If set to `"checkbox"` or `"radio"`, React Final Form will know to manage your values as a checkbox or radio button respectively. Results in a `checked` boolean inside the `input` value given to your render prop.
It will be added on your input component, or you may retrieve its value inside the "input" property of your custom components
## `validate`
```ts
(value: ?any, allValues: Object, meta: ?FieldState) => ?any
```
Optional.
A function that takes the field value, all the values of the form and the `meta` data about the field and returns an error if the value is invalid, or `undefined` if the value is valid.
⚠️ IMPORTANT ⚠️ – By default, in order to allow inline fat-arrow validation functions, the field will not rerender if you change your validation function to an alternate function that has a different behavior. If you need your field to rerender with a new validation function, you will need to update another prop on the `Field`, such as `key`. See the following sandbox for an example:
[](https://codesandbox.io/s/changing-field-level-validators-zc8ei?fontsize=14)
## `validateFields`
```ts
string[]
```
Optional.
An array of field names to validate when this field changes. If `undefined`,
_every_ field will be validated when this one changes; if `[]`, _only this
field_ will have its field-level validation function called when it changes; if
other field names are specified, those fields _and this one_ will be validated
when this field changes.
⚠️ IMPORTANT ⚠️ – By default, in order to allow inline `[]` syntax, the field will not rerender if you change your `validateFields` prop changes. If you need your field to rerender with a new `validateFields` setting, you will need to update another prop on the `Field`, such as `key`.
## `value`
```ts
any
```
Optional.
**This is only used for checkboxes and radio buttons!**
You must also include a `type="radio"` or `type="checkbox"` prop.
### Radio Buttons
The value of the radio button. The radio button will render as `checked` if and only if the value given here `===` the value for the field in the form.
### Checkboxes
#### With `value`
The checkbox will be `checked` if the value given in `value` is contained in the array that is the value for the field for the form. Checking the box will add the value to the array, and unchecking the checkbox will remove the value from the array.
#### Without `value`
The checkbox will be `checked` if the value is truthy. Checking the box will set the value to `true`, and unchecking the checkbox will set the value to `false`.
================================================
FILE: docs/types/FieldRenderProps.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FieldRenderProps). Links may not work on Github.com.
# `FieldRenderProps`
These are the props that [`<Field/>`](../api/Field) provides to your render function or component.
This object separates out the values and event handlers intended to be given to the input component from the `meta` data about the field. The `input` can be destructured directly into an `<input/>` like so: `<input {...props.input}/>`. Keep in mind that **the values in `meta` are dependent on you having subscribed to them** with the [`subscription`](FieldProps#subscription) prop.
## `input.name`
```ts
string
```
**Required**
The name of the field.
## `input.onBlur`
```ts
(?SyntheticFocusEvent<*>) => void`
```
**Required**
The `onBlur` function can take a `SyntheticFocusEvent` like it would if you had given it directly to an `<input/>` component, but you can also just call it: `props.input.onBlur()` to mark the field as blurred (inactive).
Related:
- [`SyntheticFocusEvent`](https://reactjs.org/docs/events.html#focus-events)
## `input.onChange`
```ts
(SyntheticInputEvent<*> | any) => void
```
**Required**
The `onChange` function can take a `SyntheticInputEvent` like it would if you had given it directly to an `<input/>` component (in which case it will read the value out of `event.target.value`), but you can also just call it: `props.input.onChange(value)` to update the value of the field.
Related:
- [`SyntheticInputEvent`](https://reactjs.org/docs/events.html#form-events)
## `input.onFocus`
```ts
(?SyntheticFocusEvent<*>) => void
```
**Required**
The `onFocus` function can take a `SyntheticFocusEvent` like it would if you had given it directly to an `<input/>` component, but you can also just call it: `props.input.onFocus()` to mark the field as focused (active).
Related:
- [`SyntheticFocusEvent`](https://reactjs.org/docs/events.html#focus-events)
## `input.value`
```ts
any
```
Optional. May not be present if you have not [subscribed](FieldProps#subscription) to `value`.
The current value of the field.
## `meta.active`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `active`.
Whether or not the field currently has focus.
## `meta.data`
A place for arbitrary values to be placed by mutators.
## `meta.dirty`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `dirty`.
`true` when the value of the field is not equal to the initial value (using the [`isEqual`](FieldProps#isequal) comparator provided to `<Field/>`), `false` if the values are equal.
## `meta.dirtySinceLastSubmit`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `dirtySinceLastSubmit`.
`true` when the value of the field is not equal to the value last submitted (using the [`isEqual`](FieldProps#isequal) comparator provided to `<Field/>`), `false` if the values are equal.
## `meta.error`
```ts
any
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `error`.
The current validation error for this field.
## `meta.initial`
```ts
any
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `initial`.
The initial value of the field. `undefined` if it was never initialized.
## `meta.invalid`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `invalid`.
`true` if the field has a validation error or a submission error. `false` otherwise.
## `meta.modified`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `modified`.
`true` if this field's value has ever been changed. `false` otherwise.
Once `true`, it will remain `true` for the lifetime of the field, or until the form is reset.
## `meta.modifiedSinceLastSubmit`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `modifiedSinceLastSubmit`.
`true` if this field's value has ever been changed since last submission. `false` otherwise.
Once `true`, it will remain `true` until the next submit action, or until the form is reset.
## `meta.pristine`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `pristine`.
`true` if the current value is `===` to the initial value, `false` if the values are `!==`.
## `meta.submitError`
```ts
any
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `submitError`.
The submission error for this field.
## `meta.submitFailed`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `submitFailed`.
`true` if a form submission has been tried and failed. `false` otherwise.
## `meta.submitSucceeded`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `submitSucceeded`.
`true` if the form has been successfully submitted. `false` otherwise.
## `meta.submitting`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `submitting`.
`true` if the form is currently being submitted asynchronously. `false` otherwise.
## `meta.touched`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `touched`.
`true` if this field has ever gained and lost focus. `false` otherwise.
Useful for knowing when to display error messages.
## `meta.valid`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `valid`.
`true` if this field has no validation or submission errors. `false` otherwise.
## `meta.validating`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `validating`.
`true` if this field is currently waiting on its asynchronous field-level validation function to resolve. `false` otherwise.
## `meta.visited`
```ts
boolean
```
Optional: May not be present if you have not [subscribed](FieldProps#subscription) to `visited`.
`true` if this field has ever gained focus. `false` otherwise.
================================================
FILE: docs/types/FormProps.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormProps). Links may not work on Github.com.
# `FormProps`
These are the props that you pass to [`<Form/>`](../api/Form). You must provide one of the ways to render: `component`, `render`, or `children`. The rest are mostly just passed along to Final Form's [`Config`](/docs/final-form/types/Config).
## `children`
```ts
((props: FormRenderProps) => React.Node) | React.Node`
```
Optional. (if you specify [`component`](#component) or [`render`](#render))
A render function that is given [`FormRenderProps`](FormRenderProps), as well as any non-API props passed into the `<Form/>` component. For example, if you did...
```tsx
<Form onSubmit={onSubmit} someArbitraryOtherProp={42}>
{props => {
console.log(props.someArbitraryOtherProp) // would print 42
return <form onSubmit={props.handleSubmit}> ... </form>
}}
</Form>
```
Note that if you specify [`render`](#render) _and_ `children`, `render` will be called, with `children` injected as if it were an additional prop.
Related:
- [`FormRenderProps`](FormRenderProps)
## `component`
```ts
React.ComponentType<FormRenderProps>
```
Optional. It is recommended that you use [`children`](#children) or [`render`](#render).
A component that is given [`FormRenderProps`](FormRenderProps) as props, as well as any non-API props passed into the [`<Form/>`](../api/Form) component. For example, if you did...
<!-- prettier-ignore -->
```tsx
<Form
onSubmit={onSubmit}
component={MyFormComp}
someArbitraryOtherProp={42} />
const MyFormComp = props => {
console.log(props.someArbitraryOtherProp) // would print 42
return <form onSubmit={props.handleSubmit}> ... </form>
}
```
Note that your component will be rendered using [`React.createElement()`](https://reactjs.org/docs/react-api.html#createelement) resulting in your component actually being in the React node tree, i.e. inspectable in [DevTools](https://github.com/facebook/react-devtools#react-developer-tools-).
Related:
- [`FormRenderProps`](FormRenderProps)
## `debug`
```ts
(
state: FormState,
fieldStates: { [string]: FieldState }
) => void
```
Optional.
A callback for debugging that receives the form state and the states of
all the fields. It's called _on every state change_. A typical thing to pass in
might be `console.log`.
Related:
- [`FormState`](/docs/final-form/types/FormState)
- [`FieldState`](/docs/final-form/types/FieldState)
## `decorators`
```ts
Decorator[]
```
Optional.
An array of decorators to apply to the form. [`<Form/>`](../api/Form) will undecorate the form on unmount.
Related:
- [`Decorator`](/docs/final-form/types/Decorator)
## `form`
```ts
FormApi
```
Optional. _Advanced Usage_
If you'd like to construct your own Final Form `form` instance using [`createForm()`](/docs/final-form/api#createform), you may do so and pass it into [`<Form/>`](../api/Form) as a prop. Doing so will ignore all the other config props.
Related:
- [`FormApi`](/docs/final-form/types/FormApi)
## `initialValues`
```ts
FormValues | Object
```
Optional.
The initial values of your form. These will also be used to compare against the
current values to calculate `pristine` and `dirty`.
If you are using Typescript, these values must be the same type as the object given to your [`onSubmit`](#onsubmit) function.
## `initialValuesEqual`
```ts
(Object | undefined, Object | undefined) => boolean
```
Optional.
A predicate to determine whether or not the [`initialValues`](#initialvalues) prop has changed, i.e. to know if the form needs to be reinitialized with the new values. Useful for passing in a "deep equals" function if you need to. Defaults to "shallow equals".
## `keepDirtyOnReinitialize`
```ts
boolean
```
Optional.
If `true`, only pristine values will be overwritten when `initialize(newValues)` is called. This can be useful for allowing a user to continue to edit a record while the record is being saved asynchronously, and the form is reinitialized to the saved values when the save is successful. Defaults to `false`.
## `mutators`
```ts
{ [string]: Mutator }
```
Optional.
Named mutator functions.
Related:
- [Mutator](/docs/final-form/types/Mutator)
## `onSubmit`
```ts
(
values: FormValues,
form: FormApi,
callback: ?(errors: ?Object) => void
) => ?Object | Promise<?Object> | void
```
**Required.**
Function to call when the form is submitted. There are three possible ways to
write an `onSubmit` function:
### 1. Synchronous
Returns `undefined` on success, or an `Object` of submission errors on failure.
### 2. Asynchronous with a callback
Returns `undefined`, calls `callback()` with no arguments on success, or with an `Object` of submission errors on failure.
### 3. Asynchronous with a `Promise`
Returns a `Promise<?Object>` that resolves with no value on success or _resolves_ with an `Object` of submission errors on failure. The reason it _resolves_ with errors is to leave rejection for when there is a server or communications error.
### Submission Errors
Submission errors must be in the same shape as the values of the form. You may
return a generic error for the whole form (e.g. `'Login Failed'`) using the
special [`FORM_ERROR`](/docs/final-form/api#form_error) string key.
Related:
- [`FormApi`](/docs/final-form/types/FormApi)
## `render`
<!-- prettier-ignore -->
```ts
(props: FormRenderProps) => React.Node
```
Optional. (if you specify [`component`](#component) or [`children`](#children))
A render function that is given [`FormRenderProps`](FormRenderProps), as well as any non-API props passed into the `<Form/>` component. For example, if you did...
```tsx
<Form
onSubmit={onSubmit}
someArbitraryOtherProp={42}
render={props => {
console.log(props.someArbitraryOtherProp) // would print 42
return <form onSubmit={props.handleSubmit}> ... </form>
}}
/>
```
Note that if you specify `render` _and_ [`children`](#children), `render` will be called, with `children` injected as if it were an additional prop.
Related:
- [`FormRenderProps`](FormRenderProps)
## `subscription`
```ts
{ [string]: boolean }
```
Optional. _Advanced Usage_
An object of the parts of [`FormState`](/docs/final-form/types/FormState) to subscribe to. If a subscription is provided, the [`<Form/>`](../api/Form) will only rerender when those parts of form state change.
If no `subscription` is provided, it will default to subscribing to _all_ form state changes. i.e. [`<Form/>`](../api/Form) will rerender whenever any part of the form state changes.
Related:
- [`FormState`](/docs/final-form/types/FormState)
## `validate`
```ts
(values: FormValues) => Object | Promise<Object>
```
Optional.
A whole-record validation function that takes all the values of the form and returns any validation errors. There are two possible ways to write a `validate` function:
### 1. Synchronous
Returns `{}` or `undefined` when the values are valid, or an `Object` of validation errors when the values are invalid.
### 2. Asynchronous with a `Promise`
Returns a `Promise<?Object>` that resolves with no value on success or _resolves_ with an `Object` of validation errors on failure. The reason it _resolves_ with errors is to leave _rejection_ for when there is a server or communications error.
### Validation Errors
Validation errors must be in the same shape as the values of the form. You may return a generic error for the whole form using the special [`FORM_ERROR`](/docs/final-form/api#form_error) string key.
## `validateOnBlur`
```ts
boolean
```
Optional.
If `true`, validation will happen on blur. If `false`, validation will happen on change. Defaults to `false`.
================================================
FILE: docs/types/FormRenderProps.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormRenderProps). Links may not work on Github.com.
# `FormRenderProps`
These are the props that [`<Form/>`](../api/Form) provides to your render function or component. Keep in mind that the values you receive here are dependent upon which values of [`FormState`](/docs/final-form/types/FormState) you have subscribed to with the [`subscription` prop](FormProps#subscription).
This object contains everything in Final Form's [`FormState`](/docs/final-form/types/FormState) as well as:
## `form`
```ts
FormApi
```
The Final Form [`FormApi`](/docs/final-form/types/FormApi).
## `handleSubmit`
```ts
(?SyntheticEvent<HTMLFormElement>) => ?Promise<?Object>
```
A function intended for you to give directly to the `<form>` tag:
<!-- prettier-ignore -->
```jsx
<form onSubmit={handleSubmit}>
... fields go here ...
</form>
```
The function's return type depends on the way the [`onSubmit` function is written](../types/FormProps#onsubmit).
Related:
- [`SyntheticEvent`](https://reactjs.org/docs/events.html)
================================================
FILE: docs/types/FormSpyProps.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormSpyProps). Links may not work on Github.com.
# `FormSpyProps`
These are the props that you pass to [`<FormSpy/>`](../api/FormSpy). If you do not provide an [`onChange`](#onchange) callback, you must provide one of the ways to render: [`component`](#component), [`render`](#render), or [`children`](#children).
## `children`
<!-- prettier-ignore -->
```ts
(props: FormRenderProps) => React.Node
```
Optional. (if you specify [`component`](#component) or [`render`](#render) or [`onChange`](#onchange))
A render function that is given [`FormSpyRenderProps`](FormSpyRenderProps), as well as any non-API props passed into the `<FormSpy/>` component. For example, if you did...
```tsx
<FormSpy someArbitraryOtherProp={42}>
{props => {
console.log(props.someArbitraryOtherProp) // would print 42
return <pre>{JSON.stringify(props.values, undefined, 2)}</pre>
}}
</FormSpy>
```
Note that if you specify [`render`](#render) _and_ `children`, `render` will be called, with `children` injected as if it were an additional prop.
**Will not be called if an [`onChange`](#onchange) callback is specified.**
Related:
- [`FormSpyRenderProps`](FormSpyRenderProps)
## `component`
```ts
React.ComponentType<FormSpyRenderProps>
```
Optional. It is recommended that you use [`children`](#children) or [`render`](#render).
A component that is given [`FormSpyRenderProps`](FormSpyRenderProps) as props, as well as any non-API props passed into the [`<FormSpy/>`](../api/FormSpy) component. For example, if you did...
<!-- prettier-ignore -->
```tsx
<FormSpy
component={MyFormSpyComp}
someArbitraryOtherProp={42} />
const MyFormSpyComp = props => {
console.log(props.someArbitraryOtherProp) // would print 42
return <pre>{JSON.stringify(props.values, undefined, 2)}</pre>
}
```
Note that your component will be rendered using [`React.createElement()`](https://reactjs.org/docs/react-api.html#createelement) resulting in your component actually being in the React node tree, i.e. inspectable in [DevTools](https://github.com/facebook/react-devtools#react-developer-tools-).
**Will not be called if an [`onChange`](#onchange) callback is specified.**
Related:
- [`FormSpyRenderProps`](FormSpyRenderProps)
## `onChange`
```ts
(formState: FormState) => void
```
Optional.
A change listener that will be called with form state whenever the form state, as subscribed to by the [`subscription`](#subscription) prop, has changed.
When an `onChange` prop is provided, the [`<FormSpy/>`](../api/FormSpy) will not render anything.
## `render`
<!-- prettier-ignore -->
```ts
(props: FormSpyRenderProps) => React.Node
```
Optional. (if you specify [`component`](#component) or [`children`](#children) or [`onChange`](#onchange))
A render function that is given [`FormSpyRenderProps`](FormSpyRenderProps), as well as any non-API props passed into the `<FormSpy/>` component. For example, if you did...
```tsx
<FormSpy
someArbitraryOtherProp={42}
render={props => {
console.log(props.someArbitraryOtherProp) // would print 42
return <pre>{JSON.stringify(props.values, undefined, 2)}</pre>
}}
/>
```
Note that if you specify `render` _and_ [`children`](#children), `render` will be called, with `children` injected as if it were an additional prop.
**Will not be called if an [`onChange`](#onchange) callback is specified.**
Related:
- [`FormSpyRenderProps`](FormSpyRenderProps)
## `subscription`
```ts
{ [string]: boolean }
```
Optional. _Advanced Usage_
An object of the parts of [`FormState`](/docs/final-form/types/FormState) to subscribe to. If a subscription is provided, the [`<FormSpy/>`](../api/FormSpy) will only rerender when those parts of form state change.
If no `subscription` is provided, it will default to subscribing to _all_ form state changes. i.e. [`<FormSpy/>`](../api/FormSpy) will rerender whenever any part of the form state changes.
Related:
- [`FormState`](/docs/final-form/types/FormState)
================================================
FILE: docs/types/FormSpyRenderProps.md
================================================
# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormSpyRenderProps). Links may not work on Github.com.
# `FormSpyRenderProps`
These are the props that [`<FormSpy/>`](../api/FormSpy) provides to your render function or component. Keep in mind that the values you receive here are dependent upon which values of [`FormState`](/docs/final-form/types/FormState) you have subscribed to with the [`subscription`](FormSpyProps#subscription) prop.
This object contains everything in Final Form's [`FormState`](/docs/final-form/types/FormState) as well as:
## `form`
```ts
FormApi
```
The Final Form [`FormApi`](/docs/final-form/types/FormApi).
================================================
FILE: eslint.config.mjs
================================================
import js from "@eslint/js";
import typescriptParser from "@typescript-eslint/parser";
import typescriptPlugin from "@typescript-eslint/eslint-plugin";
import reactPlugin from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import jsxA11y from "eslint-plugin-jsx-a11y";
// Manually defined globals to avoid issues with the 'globals' package
const browserGlobals = {
window: "readonly",
document: "readonly",
navigator: "readonly",
console: "readonly",
fetch: "readonly",
setTimeout: "readonly",
HTMLInputElement: "readonly",
HTMLElement: "readonly",
};
const nodeGlobals = {
process: "readonly",
require: "readonly",
module: "readonly",
exports: "writable",
__dirname: "readonly",
__filename: "readonly",
global: "readonly",
console: "readonly",
};
const jestGlobals = {
jest: "readonly",
describe: "readonly",
it: "readonly",
expect: "readonly",
afterEach: "readonly",
beforeEach: "readonly",
test: "readonly",
beforeAll: "readonly",
afterAll: "readonly",
};
export default [
// Base config for all JS/TS files (can be overridden)
{
ignores: [
"node_modules/**",
"dist/**",
"coverage/**",
"*.min.js",
"examples/**",
],
},
js.configs.recommended, // Apply ESLint recommended rules globally (respecting ignores)
// Configuration for TypeScript files in src/
{
files: ["src/**/*.{ts,tsx}"],
ignores: ["**/*.test.ts", "**/*.test.tsx"],
languageOptions: {
parser: typescriptParser,
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: "latest",
sourceType: "module",
project: "./tsconfig.json", // Project-aware linting for src files
},
globals: {
...browserGlobals,
...nodeGlobals,
...jestGlobals,
es2021: true,
},
},
plugins: {
"@typescript-eslint": typescriptPlugin,
react: reactPlugin,
"react-hooks": reactHooks,
"jsx-a11y": jsxA11y,
},
rules: {
...typescriptPlugin.configs.recommended.rules,
...reactPlugin.configs.recommended.rules,
"react/react-in-jsx-scope": "off",
"react/jsx-uses-react": "off",
"jsx-a11y/href-no-hash": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/no-unused-vars": [
"warn",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unsafe-function-type": "off",
"react/no-children-prop": "off",
"@typescript-eslint/no-unused-expressions": "off",
"no-undef": "off", // TypeScript handles this for .ts/.tsx
},
settings: {
react: { version: "detect" },
},
},
// Configuration for TypeScript test files in typescript/ (for dtslint, no project parsing)
{
files: ["typescript/**/*.ts", "typescript/**/*.tsx"],
languageOptions: {
parser: typescriptParser,
parserOptions: {
ecmaFeatures: { jsx: true }, // Allow JSX in .tsx test files
ecmaVersion: "latest",
sourceType: "module",
},
globals: {
...browserGlobals,
...nodeGlobals,
...jestGlobals,
es2021: true,
}, // General globals for TS test files
},
plugins: {
"@typescript-eslint": typescriptPlugin,
// Add other plugins if relevant for these test files, e.g., react if they use React
},
rules: {
// Lighter ruleset for .d.ts test files or general TS syntax checking
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"no-unused-vars": "off",
"no-undef": "off", // Disable no-undef for TypeScript files
},
},
// Configuration for JavaScript files (e.g., .js test files in src/, config files)
{
files: ["**/*.js", "**/*.jsx"],
ignores: ["examples/**"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
globals: {
...browserGlobals,
...nodeGlobals,
...jestGlobals,
es2021: true,
},
},
plugins: {
react: reactPlugin,
},
rules: {
"no-undef": "error",
"react/jsx-uses-vars": "warn",
"react/react-in-jsx-scope": "off",
"no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], // Enforce _ prefix for unused vars in JS files
},
},
// Configuration for .mjs files (ES modules in Node.js environment)
{
files: ["**/*.mjs"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...nodeGlobals,
},
},
rules: {
"no-undef": "error",
},
},
// Specific overrides for ALL test files (JS and TS)
{
files: ["**/*.test.js", "**/*.test.jsx", "**/*.test.ts", "**/*.test.tsx"],
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
},
},
];
================================================
FILE: examples/async-field-level-validation/Spinner.js
================================================
import styled, { keyframes } from "styled-components";
const rotation = keyframes`
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
}
`;
export default styled.div`
height: 12px;
width: 12px;
margin-left: 5px;
position: absolute;
right: 0;
top: 0;
animation: ${rotation} 0.6s infinite linear;
border-left: 6px solid rgba(0, 174, 239, 0.15);
border-right: 6px solid rgba(0, 174, 239, 0.15);
border-bottom: 6px solid rgba(0, 174, 239, 0.15);
border-top: 6px solid rgba(0, 174, 239, 0.8);
border-radius: 100%;
`;
================================================
FILE: examples/async-field-level-validation/Styles.js
================================================
import styled, { css } from "styled-components";
const btn = (light, dark) => css`
white-space: nowrap;
display: inline-block;
border-radius: 5px;
padding: 5px 15px;
font-size: 16px;
color: white;
&:visited {
color: white;
}
background-image: linear-gradient(${light}, ${dark});
border: 1px solid ${dark};
&:hover {
background-image: linear-gradient(${light}, ${dark});
&[disabled] {
background-image: linear-gradient(${light}, ${dark});
}
}
&:visited {
color: black;
}
&[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
`;
const btnDefault = css`
${btn("#ffffff", "#d5d5d5")} color: #555;
`;
const btnPrimary = btn("#4f93ce", "#285f8f");
export default styled.div`
font-family: sans-serif;
h1 {
text-align: center;
color: #222;
}
h2 {
text-align: center;
color: #222;
}
& > div {
text-align: center;
}
a {
display: block;
text-align: center;
color: #222;
margin-bottom: 10px;
}
form {
max-width: 500px;
margin: 10px auto;
border: 1px solid #ccc;
padding: 20px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
border-radius: 3px;
position: relative;
& > div {
display: flex;
flex-flow: row nowrap;
line-height: 2em;
margin: 5px;
& > label {
color: #333;
width: 110px;
font-size: 1em;
line-height: 32px;
}
& > input,
& > select,
& > textarea {
flex: 1;
padding: 3px 5px;
font-size: 1em;
margin-left: 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
& > input[type="checkbox"] {
margin-top: 7px;
}
& > div {
margin-left: 16px;
& > label {
display: block;
& > input {
margin-right: 3px;
}
}
}
& > span {
line-height: 32px;
margin-left: 10px;
color: #800;
font-weight: bold;
}
}
& > .buttons {
display: flex;
flex-flow: row nowrap;
justify-content: center;
margin-top: 15px;
}
button {
margin: 0 10px;
&[type="submit"] {
${btnPrimary};
}
&[type="button"] {
${btnDefault};
}
}
pre {
border: 1px solid #ccc;
background: rgba(0, 0, 0, 0.1);
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
padding: 20px;
}
}
`;
================================================
FILE: examples/async-field-level-validation/index.js
================================================
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import Spinner from "./Spinner";
import { Form, Field } from "react-final-form";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const onSubmit = async (values) => {
await sleep(300);
window.alert(JSON.stringify(values, undefined, 2));
};
const required = (value) => (value ? undefined : "Required");
const mustBeNumber = (value) => (isNaN(value) ? "Must be a number" : undefined);
const minValue = (min) => (value) =>
isNaN(value) || value >= min ? undefined : `Should be greater than ${min}`;
const composeValidators =
(...validators) =>
(value) =>
validators.reduce(
(error, validator) => error || validator(value),
undefined,
);
const simpleMemoize = (fn) => {
let lastArg;
let lastResult;
return (arg) => {
if (arg !== lastArg) {
lastArg = arg;
lastResult = fn(arg);
}
return lastResult;
};
};
const usernameAvailable = simpleMemoize(async (value) => {
if (!value) {
return "Required";
}
await sleep(400);
if (
~["john", "paul", "george", "ringo"].indexOf(value && value.toLowerCase())
) {
return "Username taken!";
}
});
const App = () => (
<Styles>
<h1>
<span role="img" aria-label="final form flag">
🏁
</span>{" "}
React Final Form Example
</h1>
<h2>Asynchronous Field-Level Validation</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<div>Usernames John, Paul, George or Ringo will fail async validation.</div>
<Form
onSubmit={onSubmit}
render={({ handleSubmit, reset, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<Field name="username" validate={usernameAvailable}>
{({ input, meta }) => (
<div>
<label>Username</label>
<input {...input} type="text" placeholder="Username" />
{meta.error && meta.touched && <span>{meta.error}</span>}
{meta.validating && <Spinner />}
</div>
)}
</Field>
<Field name="lastName" validate={required}>
{({ input, meta }) => (
<div>
<label>Last Name</label>
<input {...input} type="text" placeholder="Last Name" />
{meta.error && meta.touched && <span>{meta.error}</span>}
</div>
)}
</Field>
<Field
name="age"
validate={composeValidators(required, mustBeNumber, minValue(18))}
>
{({ input, meta }) => (
<div>
<label>Age</label>
<input {...input} type="text" placeholder="Age" />
{meta.error && meta.touched && <span>{meta.error}</span>}
</div>
)}
</Field>
<div className="buttons">
<button type="submit" disabled={submitting}>
Submit
</button>
<button
type="button"
onClick={reset}
disabled={submitting || pristine}
>
Reset
</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
</Styles>
);
render(<App />, document.getElementById("root"));
================================================
FILE: examples/async-field-level-validation/package.json
================================================
{
"name": "react-final-form-asynchronous-field-level-validation-example",
"version": "1.0.0",
"description": "This example demonstrates how field-level validation functions may be asynchronous.",
"keywords": [
"react-final-form",
"final-form",
"react",
"validation",
"asynchronous"
],
"main": "index.js",
"dependencies": {
"final-form": "4.20.4",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-final-form": "6.5.3",
"styled-components": "4.2.0"
}
}
================================================
FILE: examples/async-field-level-validation/readme.md
================================================
# Asynchronous Field-Level Validation
[](https://codesandbox.io/s/wy7z7q5zx5)
================================================
FILE: examples/async-redux-submission/Styles.js
================================================
import styled, { css } from "styled-components";
const btn = (light, dark) => css`
white-space: nowrap;
display: inline-block;
border-radius: 5px;
padding: 5px 15px;
font-size: 16px;
color: white;
&:visited {
color: white;
}
background-image: linear-gradient(${light}, ${dark});
border: 1px solid ${dark};
&:hover {
background-image: linear-gradient(${light}, ${dark});
&[disabled] {
background-image: linear-gradient(${light}, ${dark});
}
}
&:visited {
color: black;
}
&[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
`;
const btnDefault = css`
${btn("#ffffff", "#d5d5d5")} color: #555;
`;
const btnPrimary = btn("#4f93ce", "#285f8f");
const btnDanger = btn("#e27c79", "#c9302c");
export default styled.div`
font-family: sans-serif;
h1 {
text-align: center;
color: #222;
}
h2 {
text-align: center;
color: #222;
}
& > div {
text-align: center;
}
a {
display: block;
text-align: center;
color: #222;
margin-bottom: 10px;
}
p {
max-width: 500px;
margin: 10px auto;
& > a {
display: inline;
}
}
.loading {
font-size: 2em;
font-weight: bold;
text-align: center;
margin: 50px;
}
form,
div.form {
text-align: left;
max-width: 500px;
margin: 10px auto;
border: 1px solid #ccc;
padding: 20px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
border-radius: 3px;
position: relative;
& > div {
display: flex;
flex-flow: row nowrap;
line-height: 2em;
position: relative;
padding: 8px 5px;
border: 1px solid transparent;
&.active {
background-color: paleturquoise;
border-color: turquoise;
}
& > label {
color: #333;
width: 110px;
min-width: 60px;
font-size: 1em;
line-height: 32px;
}
& > input,
& > .downshift > input,
& > select,
& > textarea {
flex: 1;
padding: 6px 9px;
font-size: 1em;
margin-left: 15px;
border: 1px solid #ccc;
border-radius: 3px;
&[disabled] {
background: #eee;
}
}
& > input[type="checkbox"] {
margin-top: 7px;
}
& > div {
margin-left: 16px;
& > label {
margin-left: 0;
display: block;
& > input {
margin-right: 3px;
}
}
&.downshift {
margin-left: 0;
padding-left: 15px;
flex: 1;
& > input {
width: 100%;
padding: 6px 5px;
font-size: 1em;
margin-left: 0;
border: 1px solid #ccc;
border-radius: 3px;
}
}
}
& > span {
line-height: 32px;
margin-left: 10px;
color: #800;
font-weight: bold;
}
& > button.remove {
${btnDanger};
}
}
& > .buttons {
display: flex;
flex-flow: row nowrap;
justify-content: center;
margin-top: 15px;
}
.error {
display: flex;
font-weight: bold;
color: #800;
flex-flow: row nowrap;
justify-content: center;
}
pre {
position: relative;
border: 1px solid #ccc;
background: rgba(0, 0, 0, 0.1);
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
padding: 20px;
}
.submitting {
display: block;
position: absolute;
top: -5px;
left: -5px;
right: -5px;
padding: 0;
text-align: center;
background: rgba(0, 0, 0, 0.4);
color: white;
z-index: 10;
font-weight: bold;
font-size: 0.8em;
}
.saving {
font-size: 0.8em;
font-weight: bold;
color: darkblue;
margin: 8px 0 0 7px;
}
}
button {
margin: 0 10px;
&[type="submit"] {
${btnPrimary};
}
&[type="button"] {
${btnDefault};
}
}
.downshift-options {
border: 1px solid #ddd;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
& > div {
padding: 3px 5px;
}
}
`;
================================================
FILE: examples/async-redux-submission/asyncSubmissionMiddleware.js
================================================
import { REGISTER, REGISTER_SUCCESS } from "./registrationDuck";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const submit = async (values) => {
await sleep(200);
if (values.firstName === "John") {
throw Error({ firstName: "No John's Allowed!" });
}
window.alert(JSON.stringify(values, 0, 2));
};
/** This is to mimic the behavior of one of the various Redux async middlewares */
const asyncSubmissionMiddleware = (store) => (next) => (action) => {
if (action && action.type === REGISTER) {
submit(action.payload).then(
() => store.dispatch({ type: REGISTER_SUCCESS }),
(errors) => {
// NOTE!! We are passing REGISTER_SUCCESS here because 🏁 Final Form expects
// submit errors to come back in a *resolved* promise.
store.dispatch({ type: REGISTER_SUCCESS, payload: errors });
},
);
}
return next(action);
};
export default asyncSubmissionMiddleware;
================================================
FILE: examples/async-redux-submission/index.js
================================================
import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";
import store, { promiseListener } from "./store";
import {
REGISTER,
REGISTER_SUCCESS,
REGISTER_FAILURE,
} from "./registrationDuck";
import MakeAsyncFunction from "react-redux-promise-listener";
const SubmitError = ({ name }) => (
<Field
name={name}
subscription={{ submitError: true, dirtySinceLastSubmit: true }}
>
{({ meta: { submitError, dirtySinceLastSubmit } }) =>
submitError && !dirtySinceLastSubmit ? <span>{submitError}</span> : null
}
</Field>
);
const App = () => (
<Provider store={store}>
<Styles>
<h1>
<span role="img" aria-label="final form flag">
🏁
</span>{" "}
React Final Form
</h1>
<h2>Async Redux Submission</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<p>
Demonstrates how to use an async Redux side-effects library, like{" "}
<code>redux-saga</code> to manage form submissions using{" "}
<span role="img" aria-label="final form flag">
🏁
</span>{" "}
React Final Form. Uses{" "}
<a
href="https://github.com/erikras/redux-promise-listener"
target="_blank"
rel="noopener noreferrer"
>
<code>redux-promise-listener</code>
</a>{" "}
and{" "}
<a
href="https://github.com/erikras/react-redux-promise-listener"
target="_blank"
rel="noopener noreferrer"
>
<code>react-redux-promise-listener</code>
</a>{" "}
libraries.
</p>
<p>Dispatched Redux actions can be found in the console.</p>
<p>
First name <code>John</code> will fail submit validation.
</p>
<MakeAsyncFunction
listener={promiseListener}
start={REGISTER}
resolve={REGISTER_SUCCESS}
reject={REGISTER_FAILURE}
>
{(onSubmit) => (
<Form
onSubmit={onSubmit}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
/>
<SubmitError name="firstName" />
</div>
<div>
<label>Last Name</label>
<Field
name="lastName"
component="input"
type="text"
placeholder="Last Name"
/>
<SubmitError name="lastName" />
</div>
<div>
<label>Email</label>
<Field
name="email"
component="input"
type="email"
placeholder="Email"
/>
<SubmitError name="email" />
</div>
<div>
<label>Favorite Color</label>
<Field name="favoriteColor" component="select">
<option />
<option value="#ff0000">
<span role="img" aria-label="red heart">
❤️
</span>{" "}
Red
</option>
<option value="#00ff00">
<span role="img" aria-label="green heart">
💚
</span>{" "}
Green
</option>
<option value="#0000ff">
<span role="img" aria-label="blue heart">
💙
</span>{" "}
Blue
</option>
</Field>
</div>
<div className="buttons">
<button type="submit" disabled={submitting}>
Submit
</button>
<button
type="button"
onClick={form.reset}
disabled={submitting || pristine}
>
Reset
</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
)}
</MakeAsyncFunction>
</Styles>
</Provider>
);
render(<App />, document.getElementById("root"));
================================================
FILE: examples/async-redux-submission/package.json
================================================
{
"name": "react-final-form-async-redux-submission",
"version": "1.0.0",
"description": "Demonstrates how to use react-redux-promise-listener to construct a promise out of Redux actions to give to 🏁 React Final Form's onSubmit.",
"keywords": [
"form",
"redux",
"async",
"submit",
"promise"
],
"homepage": "https://codesandbox.io/s/new",
"main": "src/index.js",
"dependencies": {
"final-form": "4.20.4",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-final-form": "6.5.3",
"react-redux": "7.0.3",
"react-redux-promise-listener": "1.0.0",
"react-scripts": "3.0.1",
"redux": "4.0.1",
"redux-promise-listener": "1.1.1",
"styled-components": "4.2.0"
},
"devDependencies": {},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
================================================
FILE: examples/async-redux-submission/readme.md
================================================
# Async Redux Submission
[](https://codesandbox.io/s/x71mx66z8w)
================================================
FILE: examples/async-redux-submission/registrationDuck.js
================================================
// QUACK! This is a duck. https://github.com/erikras/ducks-modular-redux
// Actions
export const REGISTER = "final-form-examples/registration/REGISTER";
export const REGISTER_SUCCESS =
"final-form-examples/registration/REGISTER_SUCCESS";
export const REGISTER_FAILURE =
"final-form-examples/registration/REGISTER_FAILURE";
// Reducer
export default function reducer(state = {}, action = {}) {
switch (action.type) {
case REGISTER:
return {
...state,
registering: true,
};
case REGISTER_SUCCESS:
return {
...state,
registering: false,
};
case REGISTER_FAILURE:
return {
...state,
registering: false,
};
default:
return state;
}
}
// Action Creators
// Selectors
================================================
FILE: examples/async-redux-submission/store.js
================================================
import { createStore, combineReducers, applyMiddleware, compose } from "redux";
import createReduxPromiseListener from "redux-promise-listener";
import registration from "./registrationDuck";
import asyncSubmissionMiddleware from "./asyncSubmissionMiddleware";
const reduxPromiseListener = createReduxPromiseListener();
const logger = (store) => (next) => (action) => {
console.log(action);
return next(action);
};
const reducer = combineReducers({
registration,
});
const composeEnhancers =
(typeof window !== "undefined" &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
compose;
const store = createStore(
reducer,
{},
composeEnhancers(
applyMiddleware(
reduxPromiseListener.middleware,
asyncSubmissionMiddleware,
logger,
),
),
);
export const promiseListener = reduxPromiseListener; // <---------- IMPORTANT
export default store;
================================================
FILE: examples/async-typeahead-redux/GithubUserTypeahead.jsx
================================================
import React, { useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import { Field } from "react-final-form";
import { AsyncTypeahead } from "react-bootstrap-typeahead";
import useKeyword from "./useKeyword";
import { compose, propOr, pathOr } from "ramda";
import arrify from "arrify";
import { searchGithubUsers } from "./actions";
const AdaptedTypeahead = ({ input, render, meta, ...rest }) => (
<AsyncTypeahead {...input} {...rest} selected={input.value} />
);
const GithubUserTypeahead = ({ name, ...props }) => {
const { keyword, updateKeyword } = useKeyword(name);
const dispatch = useDispatch();
const getOptions = useCallback(pathOr([], [keyword, "value"]), [keyword]);
const isLoading = useCallback(pathOr(false, [keyword, "loading"]), [keyword]);
const handleOnSearch = useCallback(compose(dispatch, searchGithubUsers), [
dispatch,
]);
const options = useSelector(getOptions);
const loading = useSelector(isLoading);
return (
<Field
name={name}
component={AdaptedTypeahead}
parse={propOr(null, 0)}
format={arrify}
placeholder="Write a github username"
labelKey="login"
options={options}
onSearch={handleOnSearch}
isLoading={loading}
onInputChange={updateKeyword}
/>
);
};
export default GithubUserTypeahead;
================================================
FILE: examples/async-typeahead-redux/Styles.js
================================================
import styled, { css } from "styled-components";
const btn = (light, dark) => css`
white-space: nowrap;
display: inline-block;
border-radius: 5px;
padding: 5px 15px;
font-size: 16px;
color: white;
&:visited {
color: white;
}
background-image: linear-gradient(${light}, ${dark});
border: 1px solid ${dark};
&:hover {
background-image: linear-gradient(${light}, ${dark});
&[disabled] {
background-image: linear-gradient(${light}, ${dark});
}
}
&:visited {
color: black;
}
&[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
`;
const btnDefault = css`
${btn("#ffffff", "#d5d5d5")} color: #555;
`;
const btnPrimary = btn("#4f93ce", "#285f8f");
const btnDanger = btn("#e27c79", "#c9302c");
export default styled.div`
font-family: sans-serif;
h1 {
text-align: center;
color: #222;
}
h2 {
text-align: center;
color: #222;
}
& > div {
text-align: center;
}
a {
display: block;
text-align: center;
color: #222;
margin-bottom: 10px;
}
p {
max-width: 500px;
margin: 10px auto;
& > a {
display: inline;
}
}
form,
div.form {
text-align: left;
max-width: 500px;
margin: 10px auto;
border: 1px solid #ccc;
padding: 20px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
border-radius: 3px;
position: relative;
.loading {
text-align: center;
display: block;
position: absolute;
background: url("https://media.giphy.com/media/130AxGoOaR6t0I/giphy.gif")
center center;
background-size: fill;
font-size: 2em;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 50px 0 0 0;
z-index: 2;
}
& > div {
display: flex;
flex-flow: row nowrap;
line-height: 2em;
margin: 5px;
position: relative;
& > label {
color: #333;
width: 110px;
min-width: 60px;
font-size: 1em;
line-height: 32px;
}
& > input,
& > select,
& > textarea {
flex: 1;
padding: 3px 5px;
font-size: 1em;
margin-left: 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
& > input[type="checkbox"] {
margin-top: 7px;
}
& > div {
margin-left: 16px;
& > label {
display: block;
& > input {
margin-right: 3px;
}
}
}
& > span {
line-height: 32px;
margin-left: 10px;
color: #800;
font-weight: bold;
}
& > button.remove {
${btnDanger};
}
}
& > .buttons {
display: flex;
flex-flow: row nowrap;
justify-content: center;
margin-top: 15px;
}
.error {
display: flex;
font-weight: bold;
color: #800;
flex-flow: row nowrap;
justify-content: center;
}
pre {
position: relative;
border: 1px solid #ccc;
background: rgba(0, 0, 0, 0.1);
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
padding: 20px;
}
.submitting {
display: block;
position: absolute;
top: -5px;
left: -5px;
right: -5px;
padding: 0;
text-align: center;
background: rgba(0, 0, 0, 0.4);
color: white;
z-index: 10;
font-weight: bold;
font-size: 0.8em;
}
.saving {
font-size: 0.8em;
font-weight: bold;
color: darkblue;
margin: 8px 0 0 7px;
}
}
button {
margin: 0 10px;
&[type="submit"] {
${btnPrimary};
}
&[type="button"] {
${btnDefault};
}
}
`;
================================================
FILE: examples/async-typeahead-redux/actions.js
================================================
export const requestGithubUsers = (query) => ({
type: "GITHUB_USERS_REQUEST",
query,
});
export const storeGithubUsers = (query, users) => ({
type: "GITHUB_USERS_RESPONSE",
query,
users,
});
export const searchGithubUsers = (query) => (dispatch) => {
dispatch(requestGithubUsers(query));
fetch(`https://api.github.com/search/users?q=${query}`)
.then((res) => res.json())
.then(({ items: users }) => dispatch(storeGithubUsers(query, users)));
};
================================================
FILE: examples/async-typeahead-redux/index.js
================================================
import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import Styles from "./Styles";
import { Form } from "react-final-form";
import setFieldData from "final-form-set-field-data";
import configureStore from "./store";
import GithubUserTypeahead from "./GithubUserTypeahead";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const onSubmit = async (values) => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const store = configureStore();
const App = () => (
<Styles>
<h1>
<span role="img" aria-label="final form flag">
🏁
</span>{" "}
React Final Form
</h1>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<Form
onSubmit={onSubmit}
initialValues={{ user1: null, user2: null }}
mutators={{ setFieldData }}
render={({ handleSubmit, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<div>
<label>Github users</label>
<GithubUserTypeahead name="user1" />
</div>
<div>
<label>Github users 2</label>
<GithubUserTypeahead name="user2" />
</div>
<div className="buttons">
<button type="submit" disabled={submitting || pristine}>
Submit
</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
</Styles>
);
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root"),
);
================================================
FILE: examples/async-typeahead-redux/package.json
================================================
{
"name": "react-final-form-github-user-asynctypeahead",
"version": "1.0.0",
"description": "Demonstrates how extensible 🏁 React Final Form can be with the combination of FormSpy and setFieldData by creating a totally separate validation engine that runs when a field is blurred.",
"keywords": [],
"main": "index.js",
"dependencies": {
"arrify": "2.0.1",
"final-form": "4.20.4",
"final-form-set-field-data": "1.0.2",
"ramda": "0.25.0",
"react": "17.0.2",
"react-bootstrap-typeahead": "3.1.4",
"react-dom": "17.0.2",
"react-final-form": "6.5.3",
"react-redux": "7.1.0",
"redux": "4.0.4",
"redux-thunk": "2.3.0",
"styled-components": "2.4.0"
}
}
================================================
FILE: examples/async-typeahead-redux/readme.md
================================================
# Async Typeahead and Redux
[](https://codesandbox.io/s/5m4w2909k)
================================================
FILE: examples/async-typeahead-redux/store.js
================================================
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
const initialState = {};
const reducer = (state = initialState, action) => {
switch (action.type) {
case "GITHUB_USERS_REQUEST":
return {
...state,
[action.query]: {
...state[action.query],
loading: true,
},
};
case "GITHUB_USERS_RESPONSE":
return {
...state,
[action.query]: {
value: action.users,
loading: false,
},
};
default:
return state;
}
};
export default () => createStore(reducer, undefined, applyMiddleware(thunk));
================================================
FILE: examples/async-typeahead-redux/useKeyword.js
================================================
import { useForm, useField } from "react-final-form";
import { propOr } from "ramda";
const useKeyword = (name) => {
const {
mutators: { setFieldData },
} = useForm();
const { meta } = useField(name, { subscription: { data: true } });
return {
keyword: propOr(null, "keyword", meta.data),
updateKeyword: (keyword) => setFieldData(name, { keyword }),
};
};
export default useKeyword;
================================================
FILE: examples/auto-save-field-blur/AutoSave.js
================================================
import React from "react";
import { FormSpy } from "react-final-form";
import diff from "object-diff";
class AutoSave extends React.Component {
constructor(props) {
super(props);
this.state = { values: props.values, submitting: false };
}
componentWillReceiveProps(nextProps) {
if (this.props.active && this.props.active !== nextProps.active) {
// blur occurred
this.save(this.props.active);
}
}
save = async (blurredField) => {
if (this.promise) {
await this.promise;
}
const { values, setFieldData, save } = this.props;
// This diff step is totally optional
const difference = diff(this.state.values, values);
if (Object.keys(difference).length) {
// values have changed
this.setState({ submitting: true, values });
setFieldData(blurredField, { saving: true });
this.promise = save(difference);
await this.promise;
delete this.promise;
this.setState({ submitting: false });
setFieldData(blurredField, { saving: false });
}
};
render() {
// This component doesn't have to render anything, but it can render
// submitting state.
return null;
}
}
// Make a HOC
// This is not the only way to accomplish auto-save, but it does let us:
// - Use built-in React lifecycle methods to listen for changes
// - Maintain state of when we are submitting
// - Render a message when submitting
// - Pass in save prop nicely
export default (props) => (
<FormSpy
{...props}
subscription={{ active: true, values: true }}
component={AutoSave}
/>
);
================================================
FILE: examples/auto-save-field-blur/Styles.js
================================================
import styled, { css } from "styled-components";
const btn = (light, dark) => css`
white-space: nowrap;
display: inline-block;
border-radius: 5px;
padding: 5px 15px;
font-size: 16px;
color: white;
&:visited {
color: white;
}
background-image: linear-gradient(${light}, ${dark});
border: 1px solid ${dark};
&:hover {
background-image: linear-gradient(${light}, ${dark});
&[disabled] {
background-image: linear-gradient(${light}, ${dark});
}
}
&:visited {
color: black;
}
&[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
`;
const btnDefault = css`
${btn("#ffffff", "#d5d5d5")} color: #555;
`;
const btnPrimary = btn("#4f93ce", "#285f8f");
const btnDanger = btn("#e27c79", "#c9302c");
export default styled.div`
font-family: sans-serif;
h1 {
text-align: center;
color: #222;
}
h2 {
text-align: center;
color: #222;
}
& > div {
text-align: center;
}
a {
display: block;
text-align: center;
color: #222;
margin-bottom: 10px;
}
p {
max-width: 500px;
margin: 10px auto;
& > a {
display: inline;
}
}
form,
div.form {
text-align: left;
max-width: 500px;
margin: 10px auto;
border: 1px solid #ccc;
padding: 20px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
border-radius: 3px;
position: relative;
.loading {
text-align: center;
display: block;
position: absolute;
background: url("https://media.giphy.com/media/130AxGoOaR6t0I/giphy.gif")
center center;
background-size: fill;
font-size: 2em;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 50px 0 0 0;
z-index: 2;
}
& > div {
display: flex;
flex-flow: row nowrap;
line-height: 2em;
margin: 5px;
position: relative;
& > label {
color: #333;
width: 110px;
min-width: 60px;
font-size: 1em;
line-height: 32px;
}
& > input,
& > select,
& > textarea {
flex: 1;
padding: 3px 5px;
font-size: 1em;
margin-left: 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
& > input[type="checkbox"] {
margin-top: 7px;
}
& > div {
margin-left: 16px;
& > label {
display: block;
& > input {
margin-right: 3px;
}
}
}
& > span {
line-height: 32px;
margin-left: 10px;
color: #800;
font-weight: bold;
}
& > button.remove {
${btnDanger};
}
}
& > .buttons {
display: flex;
flex-flow: row nowrap;
justify-content: center;
margin-top: 15px;
}
.error {
display: flex;
font-weight: bold;
color: #800;
flex-flow: row nowrap;
justify-content: center;
}
pre {
position: relative;
border: 1px solid #ccc;
background: rgba(0, 0, 0, 0.1);
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
padding: 20px;
}
.submitting {
display: block;
position: absolute;
top: -5px;
left: -5px;
right: -5px;
padding: 0;
text-align: center;
background: rgba(0, 0, 0, 0.4);
color: white;
z-index: 10;
font-weight: bold;
font-size: 0.8em;
}
.saving {
font-size: 0.8em;
font-weight: bold;
color: darkblue;
margin: 0 0 0 8px;
}
}
button {
margin: 0 10px;
&[type="submit"] {
${btnPrimary};
}
&[type="button"] {
${btnDefault};
}
}
`;
================================================
FILE: examples/auto-save-field-blur/index.js
================================================
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";
import setFieldData from "final-form-set-field-data";
import AutoSave from "./AutoSave";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const save = async (values) => {
console.log("Saving", values);
await sleep(1000);
};
const SavingIndicator = ({ name }) => (
<Field
name={name}
subscription={{ data: true }}
render={({
meta: {
data: { saving },
},
}) => (saving ? <div className="saving">Saving</div> : null)}
/>
);
const App = () => (
<Styles>
<h1>
<span role="img" aria-label="final form flag">
🏁
</span>{" "}
React Final Form
</h1>
<h2>Auto-Save on Field Blur</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<p>
The <code>AutoSave</code> component uses{" "}
<a
href="https://github.com/final-form/react-final-form#formspy--reactcomponenttypeformspyprops"
target="_blank"
rel="noopener noreferrer"
>
<code>FormSpy</code>
</a>{" "}
to listen to changes to values and which field is currently active and
auto-saves changes when a field is blurred. Look in the console for the
save events.
</p>
<Form
onSubmit={save /* NOT USED, but required */}
initialValues={{ employed: true, stooge: "larry" }}
mutators={{ setFieldData }}
subscription={{} /* No need to subscribe to anything */}
>
{({ form }) => (
<div className="form">
{/* Don't even need a <form> tag */}
{/* 👇 👇 👇 👇 */}
<AutoSave setFieldData={form.mutators.setFieldData} save={save} />
{/* ☝️ ️☝️ ️☝️ ️️️️☝️ ️️*/}
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
/>
<SavingIndicator name="firstName" />
</div>
<div>
<label>Last Name</label>
<Field
name="lastName"
component="input"
type="text"
placeholder="Last Name"
/>
<SavingIndicator name="lastName" />
</div>
<div>
<label>Email</label>
<Field
name="email"
component="input"
type="email"
placeholder="Email"
/>
<SavingIndicator name="email" />
</div>
<div>
<label>Favorite Color</label>
<Field name="favoriteColor" component="select">
<option />
<option value="#ff0000">
<span role="img" aria-label="red heart">
❤️
</span>{" "}
Red
</option>
<option value="#00ff00">
<span role="img" aria-label="green heart">
💚
</span>{" "}
Green
</option>
<option value="#0000ff">
<span role="img" aria-label="blue heart">
💙
</span>{" "}
Blue
</option>
</Field>
<SavingIndicator name="favoriteColor" />
</div>
<div>
<label>Employed?</label>
<Field name="employed" component="input" type="checkbox" />
<SavingIndicator name="employed" />
</div>
<div>
<label>Toppings</label>
<Field name="toppings" component="select" multiple>
<option value="ham">
<span role="img" aria-label="pig head">
🐷
</span>{" "}
Ham
</option>
<option value="mushrooms">
<span role="img" aria-label="mushroom">
🍄
</span>{" "}
Mushrooms
</option>
<option value="cheese">
<span role="img" aria-label="cheese">
🧀
</span>{" "}
Cheese
</option>
<option value="chicken">
<span role="img" aria-label="chicken">
🐓
</span>{" "}
Chicken
</option>
<option value="pineapple">
<span role="img" aria-label="pineapple">
🍍
</span>{" "}
Pinapple
</option>
</Field>
<SavingIndicator name="toppings" />
</div>
<div>
<label>Best Stooge?</label>
<div>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="larry"
/>{" "}
Larry
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="moe"
/>{" "}
Moe
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="curly"
/>{" "}
Curly
</label>
</div>
<SavingIndicator name="stooge" />
</div>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
<SavingIndicator name="notes" />
</div>
</div>
)}
</Form>
</Styles>
);
render(<App />, document.getElementById("root"));
================================================
FILE: examples/auto-save-field-blur/package.json
================================================
{
"name": "react-final-form-auto-save-on-field-blur",
"version": "1.0.0",
"description": "Demonstrates how to create an AutoSave component to listen to form state and automatically save data when fields blur.",
"keywords": [],
"main": "index.js",
"dependencies": {
"final-form": "4.20.4",
"final-form-set-field-data": "1.0.2",
"object-diff": "0.0.4",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-final-form": "6.5.3",
"styled-components": "4.2.0"
}
}
================================================
FILE: examples/auto-save-field-blur/readme.md
================================================
# Auto-Save on Field Blur
[](https://codesandbox.io/s/7k742qpo36)
================================================
FILE: examples/auto-save-selective-debounce/AutoSave.js
================================================
import React from "react";
import { FormSpy } from "react-final-form";
import diff from "object-diff";
const areObjectsIdentical = (a, b) =>
Object.keys(diff(a, b)).length === 0 && Object.keys(diff(b, a)).length === 0;
class AutoSave extends React.Component {
static defaultProps = {
debounced: [],
};
constructor(props) {
super(props);
this.state = {
submitting: false,
...this.splitValues(props.values),
};
}
componentDidUpdate() {
const { values, debounce } = this.props;
const { debouncedValues, immediateValues } = this.splitValues(values);
if (!areObjectsIdentical(this.state.immediateValues, immediateValues)) {
this.save();
}
if (!areObjectsIdentical(this.state.debouncedValues, debouncedValues)) {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.save();
}, debounce);
}
}
splitValues = (values) => {
const { debounced } = this.props;
const debouncedValues = {};
const immediateValues = {};
Object.keys(values).forEach((key) => {
if (debounced.includes(key)) {
debouncedValues[key] = values[key];
} else {
immediateValues[key] = values[key];
}
});
return {
debouncedValues,
immediateValues,
};
};
save = async () => {
if (this.promise) {
await this.promise;
}
const { save, values } = this.props;
const { debouncedValues, immediateValues } = this.splitValues(values);
this.setState(
(state) => ({
submitting: true,
immediateValues: { ...immediateValues },
debouncedValues: { ...debouncedValues },
}),
async () => {
this.promise = save({
...this.state.immediateValues,
...this.state.debouncedValues,
});
await this.promise;
delete this.promise;
this.setState({ submitting: false });
},
);
};
render() {
// This component doesn't have to render anything, but it can render
// submitting state.
return (
this.state.submitting && <div className="submitting">Submitting...</div>
);
}
}
// Make a HOC
// This is not the only way to accomplish auto-save, but it does let us:
// - Use built-in React lifecycle methods to listen for changes
// - Maintain state of when we are submitting
// - Render a message when submitting
// - Pass in debounce and save props nicely
export default (props) => (
<FormSpy {...props} subscription={{ values: true }} component={AutoSave} />
);
================================================
FILE: examples/auto-save-selective-debounce/Styles.js
================================================
import styled, { css } from "styled-components";
const btn = (light, dark) => css`
white-space: nowrap;
display: inline-block;
border-radius: 5px;
padding: 5px 15px;
font-size: 16px;
color: white;
&:visited {
color: white;
}
background-image: linear-gradient(${light}, ${dark});
border: 1px solid ${dark};
&:hover {
background-image: linear-gradient(${light}, ${dark});
&[disabled] {
background-image: linear-gradient(${light}, ${dark});
}
}
&:visited {
color: black;
}
&[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
`;
const btnDefault = css`
${btn("#ffffff", "#d5d5d5")} color: #555;
`;
const btnPrimary = btn("#4f93ce", "#285f8f");
const btnDanger = btn("#e27c79", "#c9302c");
export default styled.div`
font-family: sans-serif;
h1 {
text-align: center;
color: #222;
}
h2 {
text-align: center;
color: #222;
}
& > div {
text-align: center;
}
a {
display: block;
text-align: center;
color: #222;
margin-bottom: 10px;
}
p {
max-width: 500px;
margin: 10px auto;
& > a {
display: inline;
}
}
form,
div.form {
text-align: left;
max-width: 500px;
margin: 10px auto;
border: 1px solid #ccc;
padding: 20px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
border-radius: 3px;
position: relative;
.loading {
text-align: center;
display: block;
position: absolute;
background: url("https://media.giphy.com/media/130AxGoOaR6t0I/giphy.gif")
center center;
background-size: fill;
font-size: 2em;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 50px 0 0 0;
z-index: 2;
}
& > div {
display: flex;
flex-flow: row nowrap;
line-height: 2em;
margin: 5px;
position: relative;
& > label {
color: #333;
width: 110px;
min-width: 60px;
font-size: 1em;
line-height: 32px;
}
& > input,
& > select,
& > textarea {
flex: 1;
padding: 3px 5px;
font-size: 1em;
margin-left: 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
& > input[type="checkbox"] {
margin-top: 7px;
}
& > div {
margin-left: 16px;
& > label {
display: block;
& > input {
margin-right: 3px;
}
}
}
& > span {
line-height: 32px;
margin-left: 10px;
color: #800;
font-weight: bold;
}
& > button.remove {
${btnDanger};
}
}
& > .buttons {
display: flex;
flex-flow: row nowrap;
justify-content: center;
margin-top: 15px;
}
.error {
display: flex;
font-weight: bold;
color: #800;
flex-flow: row nowrap;
justify-content: center;
}
pre {
position: relative;
border: 1px solid #ccc;
background: rgba(0, 0, 0, 0.1);
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
padding: 20px;
}
.submitting {
display: block;
position: absolute;
top: -5px;
left: -5px;
right: -5px;
padding: 0;
text-align: center;
background: rgba(0, 0, 0, 0.4);
color: white;
z-index: 10;
font-weight: bold;
font-size: 0.8em;
}
}
button {
margin: 0 10px;
&[type="submit"] {
${btnPrimary};
}
&[type="button"] {
${btnDefault};
}
}
`;
================================================
FILE: examples/auto-save-selective-debounce/index.js
================================================
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";
import AutoSave from "./AutoSave";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const save = async (values) => {
console.log("Saving", values);
await sleep(2000);
};
const App = () => (
<Styles>
<h1>
<span role="img" aria-label="final form flag">
🏁
</span>{" "}
React Final Form
</h1>
<h2>
Auto-Save with <em>Selective</em> Debounce
</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<p>
The <code>AutoSave</code> component uses{" "}
<a
href="https://github.com/final-form/react-final-form#formspy--reactcomponenttypeformspyprops"
target="_blank"
rel="noopener noreferrer"
>
<code>FormSpy</code>
</a>{" "}
to listen to changes to values and auto-save changes. But it only
debounces changes to a specific list of fields given to the{" "}
<code>AutoSave</code> component, in this case, all the text inputs. Look
in the console for the save events.
</p>
<Form
onSubmit={save /* NOT USED, but required */}
initialValues={{ employed: true, stooge: "larry" }}
subscription={{} /* No need to subscribe to anything */}
>
{() => (
<div className="form">
{/* Don't even need a <form> tag */}
{/* 👇 👇 👇 👇 */}
<AutoSave
debounce={2000}
save={save}
debounced={["firstName", "lastName", "email", "notes"]}
/>
{/* ☝️ ️☝️ ️☝️ ️️️️☝️ ️️*/}
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
/>
</div>
<div>
<label>Last Name</label>
<Field
name="lastName"
component="input"
type="text"
placeholder="Last Name"
/>
</div>
<div>
<label>Email</label>
<Field
name="email"
component="input"
type="email"
placeholder="Email"
/>
</div>
<div>
<label>Favorite Color</label>
<Field name="favoriteColor" component="select">
<option />
<option value="#ff0000">
<span role="img" aria-label="red heart">
❤️
</span>{" "}
Red
</option>
<option value="#00ff00">
<span role="img" aria-label="green heart">
💚
</span>{" "}
Green
</option>
<option value="#0000ff">
<span role="img" aria-label="blue heart">
💙
</span>{" "}
Blue
</option>
</Field>
</div>
<div>
<label>Employed?</label>
<Field name="employed" component="input" type="checkbox" />
</div>
<div>
<label>Toppings</label>
<Field name="toppings" component="select" type="select" multiple>
<option value="ham">
<span role="img" aria-label="pig head">
🐷
</span>{" "}
Ham
</option>
<option value="mushrooms">
<span role="img" aria-label="mushroom">
🍄
</span>{" "}
Mushrooms
</option>
<option value="cheese">
<span role="img" aria-label="cheese">
🧀
</span>{" "}
Cheese
</option>
<option value="chicken">
<span role="img" aria-label="chicken">
🐓
</span>{" "}
Chicken
</option>
<option value="pineapple">
<span role="img" aria-label="pineapple">
🍍
</span>{" "}
Pinapple
</option>
</Field>
</div>
<div>
<label>Best Stooge?</label>
<div>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="larry"
/>{" "}
Larry
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="moe"
/>{" "}
Moe
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="curly"
/>{" "}
Curly
</label>
</div>
</div>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
</div>
</div>
)}
</Form>
</Styles>
);
render(<App />, document.getElementById("root"));
================================================
FILE: examples/auto-save-selective-debounce/package.json
================================================
{
"name": "react-final-form-auto-save-with-selective-debounce",
"version": "1.0.0",
"description": "Demonstrates how to use a FormSpy component to auto-save your form values with a debounce, restricting which fields are debounced.",
"keywords": [],
"main": "index.js",
"dependencies": {
"final-form": "4.20.4",
"object-diff": "0.0.4",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-final-form": "6.5.3",
"styled-components": "4.2.0"
}
}
================================================
FILE: examples/auto-save-selective-debounce/readme.md
================================================
# Auto-Save with _Selective_ Debounce
[](https://codesandbox.io/s/98j0v46zj4)
================================================
FILE: examples/auto-save-with-debounce/AutoSave.js
================================================
import React from "react";
import { FormSpy } from "react-final-form";
import diff from "object-diff";
class AutoSave extends React.Component {
constructor(props) {
super(props);
this.state = { values: props.values, submitting: false };
}
componentWillReceiveProps(nextProps) {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(this.save, this.props.debounce);
}
save = async () => {
if (this.promise) {
await this.promise;
}
const { values, save } = this.props;
// This diff step is totally optional
const difference = diff(this.state.values, values);
if (Object.keys(difference).length) {
// values have changed
this.setState({ submitting: true, values });
this.promise = save(difference);
await this.promise;
delete this.promise;
this.setState({ submitting: false });
}
};
render() {
// This component doesn't have to render anything, but it can render
// submitting state.
return (
this.state.submitting && <div className="submitting">Submitting...</div>
);
}
}
// Make a HOC
// This is not the only way to accomplish auto-save, but it does let us:
// - Use built-in React lifecycle methods to listen for changes
// - Maintain state of when we are submitting
// - Render a message when submitting
// - Pass in debounce and save props nicely
export default (props) => (
<FormSpy {...props} subscription={{ values: true }} component={AutoSave} />
);
================================================
FILE: examples/auto-save-with-debounce/Styles.js
================================================
import styled, { css } from "styled-components";
const btn = (light, dark) => css`
white-space: nowrap;
display: inline-block;
border-radius: 5px;
padding: 5px 15px;
font-size: 16px;
color: white;
&:visited {
color: white;
}
background-image: linear-gradient(${light}, ${dark});
border: 1px solid ${dark};
&:hover {
background-image: linear-gradient(${light}, ${dark});
&[disabled] {
background-image: linear-gradient(${light}, ${dark});
}
}
&:visited {
color: black;
}
&[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
`;
const btnDefault = css`
${btn("#ffffff", "#d5d5d5")} color: #555;
`;
const btnPrimary = btn("#4f93ce", "#285f8f");
const btnDanger = btn("#e27c79", "#c9302c");
export default styled.div`
font-family: sans-serif;
h1 {
text-align: center;
color: #222;
}
h2 {
text-align: center;
color: #222;
}
& > div {
text-align: center;
}
a {
display: block;
text-align: center;
color: #222;
margin-bottom: 10px;
}
p {
max-width: 500px;
margin: 10px auto;
& > a {
display: inline;
}
}
form,
div.form {
text-align: left;
max-width: 500px;
margin: 10px auto;
border: 1px solid #ccc;
padding: 20px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
border-radius: 3px;
position: relative;
.loading {
text-align: center;
display: block;
position: absolute;
background: url("https://media.giphy.com/media/130AxGoOaR6t0I/giphy.gif")
center center;
background-size: fill;
font-size: 2em;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 50px 0 0 0;
z-index: 2;
}
& > div {
display: flex;
flex-flow: row nowrap;
line-height: 2em;
margin: 5px;
position: relative;
& > label {
color: #333;
width: 110px;
min-width: 60px;
font-size: 1em;
line-height: 32px;
}
& > input,
& > select,
& > textarea {
flex: 1;
padding: 3px 5px;
font-size: 1em;
margin-left: 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
& > input[type="checkbox"] {
margin-top: 7px;
}
& > div {
margin-left: 16px;
& > label {
display: block;
& > input {
margin-right: 3px;
}
}
}
& > span {
line-height: 32px;
margin-left: 10px;
color: #800;
font-weight: bold;
}
& > button.remove {
${btnDanger};
}
}
& > .buttons {
display: flex;
flex-flow: row nowrap;
justify-content: center;
margin-top: 15px;
}
.error {
display: flex;
font-weight: bold;
color: #800;
flex-flow: row nowrap;
justify-content: center;
}
pre {
position: relative;
border: 1px solid #ccc;
background: rgba(0, 0, 0, 0.1);
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
padding: 20px;
}
.submitting {
display: block;
position: absolute;
top: -5px;
left: -5px;
right: -5px;
padding: 0;
text-align: center;
background: rgba(0, 0, 0, 0.4);
color: white;
z-index: 10;
font-weight: bold;
font-size: 0.8em;
}
}
button {
margin: 0 10px;
&[type="submit"] {
${btnPrimary};
}
&[type="button"] {
${btnDefault};
}
}
`;
================================================
FILE: examples/auto-save-with-debounce/index.js
================================================
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";
import AutoSave from "./AutoSave";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const save = async (values) => {
console.log("Saving", values);
await sleep(2000);
};
const App = () => (
<Styles>
<h1>
<span role="img" aria-label="final form flag">
🏁
</span>{" "}
React Final Form
</h1>
<h2>Auto-Save with Debounce</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<p>
The <code>AutoSave</code> component uses{" "}
<a
href="https://github.com/final-form/react-final-form#formspy--reactcomponenttypeformspyprops"
target="_blank"
rel="noopener noreferrer"
>
<code>FormSpy</code>
</a>{" "}
to listen to changes to values and auto-save changes. Look in the console
for the save events.
</p>
<Form
onSubmit={save /* NOT USED, but required */}
initialValues={{ employed: true, stooge: "larry" }}
subscription={{} /* No need to subscribe to anything */}
>
{() => (
<div className="form">
{/* Don't even need a <form> tag */}
{/* 👇 👇 👇 👇 */}
<AutoSave debounce={1000} save={save} />
{/* ☝️ ️☝️ ️☝️ ️️️️☝️ ️️*/}
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
/>
</div>
<div>
<label>Last Name</label>
<Field
name="lastName"
component="input"
type="text"
placeholder="Last Name"
/>
</div>
<div>
<label>Email</label>
<Field
name="email"
component="input"
type="email"
placeholder="Email"
/>
</div>
<div>
<label>Favorite Color</label>
<Field name="favoriteColor" component="select">
<option />
<option value="#ff0000">
<span role="img" aria-label="red heart">
❤️
</span>{" "}
Red
</option>
<option value="#00ff00">
<span role="img" aria-label="green heart">
💚
</span>{" "}
Green
</option>
<option value="#0000ff">
<span role="img" aria-label="blue heart">
💙
</span>{" "}
Blue
</option>
</Field>
</div>
<div>
<label>Employed?</label>
<Field name="employed" component="input" type="checkbox" />
</div>
<div>
<label>Toppings</label>
<Field name="toppings" component="select" multiple>
<option value="ham">
<span role="img" aria-label="pig head">
🐷
</span>{" "}
Ham
</option>
<option value="mushrooms">
<span role="img" aria-label="mushroom">
🍄
</span>{" "}
Mushrooms
</option>
<option value="cheese">
<span role="img" aria-label="cheese">
🧀
</span>{" "}
Cheese
</option>
<option value="chicken">
<span role="img" aria-label="chicken">
🐓
</span>{" "}
Chicken
</option>
<option value="pineapple">
<span role="img" aria-label="pineapple">
🍍
</span>{" "}
Pinapple
</option>
</Field>
</div>
<div>
<label>Best Stooge?</label>
<div>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="larry"
/>{" "}
Larry
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="moe"
/>{" "}
Moe
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="curly"
/>{" "}
Curly
</label>
</div>
</div>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
</div>
</div>
)}
</Form>
</Styles>
);
render(<App />, document.getElementById("root"));
================================================
FILE: examples/auto-save-with-debounce/package.json
================================================
{
"name": "react-final-form-auto-save-with-debounce",
"version": "1.0.0",
"description": "Demonstrates how to use a FormSpy component to auto-save your form values with a debounce.",
"keywords": [
"final-form",
"autosave",
"form",
"debounce",
"react-final-form"
],
"main": "index.js",
"dependencies": {
"final-form": "4.20.4",
"object-diff": "0.0.4",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-final-form": "6.5.3",
"styled-components": "4.2.0"
}
}
================================================
FILE: examples/auto-save-with-debounce/readme.md
================================================
# Auto-Save with Debounce
[](https://codesandbox.io/s/5w4yrpyo7k)
================================================
FILE: examples/calculated-fields/Styles.js
================================================
import styled, { css } from "styled-components";
const btn = (light, dark) => css`
white-space: nowrap;
display: inline-block;
border-radius: 5px;
padding: 5px 15px;
font-size: 16px;
color: white;
&:visited {
color: white;
}
background-image: linear-gradient(${light}, ${dark});
border: 1px solid ${dark};
&:hover {
background-image: linear-gradient(${light}, ${dark});
&[disabled] {
background-image: linear-gradient(${light}, ${dark});
}
}
&:visited {
color: black;
}
&[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
`;
const btnDefault = css`
${btn("#ffffff", "#d5d5d5")} color: #555;
`;
const btnPrimary = btn("#4f93ce", "#285f8f");
const btnDanger = btn("#e27c79", "#c9302c");
export default styled.div`
font-family: sans-serif;
h1 {
text-align: center;
color: #222;
}
h2 {
text-align: center;
color: #222;
}
& > div {
text-align: center;
}
a {
display: block;
text-align: center;
color: #222;
margin-bottom: 10px;
}
p {
max-width: 500px;
margin: 10px auto;
& > a {
display: inline;
}
}
form {
max-width: 500px;
margin: 10px auto;
border: 1px solid #ccc;
padding: 20px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
border-radius: 3px;
position: relative;
.loading {
text-align: center;
display: block;
position: absolute;
background: url("https://media.giphy.com/media/130AxGoOaR6t0I/giphy.gif")
center center;
background-size: fill;
font-size: 2em;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 50px 0 0 0;
z-index: 2;
}
& > div {
display: flex;
flex-flow: row nowrap;
line-height: 2em;
margin: 5px;
position: relative;
& > label {
color: #333;
width: 110px;
min-width: 60px;
font-size: 1em;
line-height: 32px;
}
& > input,
& > select,
& > textarea {
flex: 1;
padding: 3px 5px;
font-size: 1em;
margin-left: 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
& > input[type="checkbox"] {
margin-top: 7px;
}
& > div {
margin-left: 16px;
& > label {
display: block;
& > input {
margin-right: 3px;
}
}
}
& > span {
line-height: 32px;
margin-left: 10px;
color: #800;
font-weight: bold;
}
& > button.remove {
${btnDanger};
}
}
& > .buttons {
display: flex;
flex-flow: row nowrap;
justify-content: center;
margin-top: 15px;
}
button {
margin: 0 10px;
&[type="submit"] {
${btnPrimary};
}
&[type="button"] {
${btnDefault};
}
}
.error {
display: flex;
font-weight: bold;
color: #800;
flex-flow: row nowrap;
justify-content: center;
}
pre {
position: relative;
border: 1px solid #ccc;
background: rgba(0, 0, 0, 0.1);
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
padding: 20px;
}
}
`;
================================================
FILE: examples/calculated-fields/index.js
================================================
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";
import createDecorator from "final-form-calculate";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const onSubmit = async (values) => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const calculator = createDecorator(
{
field: "minimum", // when minimum changes...
updates: {
// ...update maximum to the result of this function
maximum: (minimumValue, allValues) =>
Math.max(minimumValue || 0, allValues.maximum || 0),
},
},
{
field: "maximum", // when maximum changes...
updates: {
// update minimum to the result of this function
minimum: (maximumValue, allValues) =>
Math.min(maximumValue || 0, allValues.minimum || 0),
},
},
{
field: /day\[\d\]/, // when a field matching this pattern changes...
updates: {
// ...update the total to the result of this function
total: (ignoredValue, allValues) =>
(allValues.day || []).reduce(
(sum, value) => sum + Number(value || 0),
0,
),
},
},
);
const App = () => (
<Styles>
<h1>
<span role="img" aria-label="final form flag">
🏁
</span>{" "}
React Final Form Example
</h1>
<h2>Calculated Fields</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<p>
Change the minimum and maximum values with the arrow keys and notice that
the other updates so that minimum is always <= maximum.
</p>
<p>
As you enter numbers for each day of the week, the total is calulated in
realtime.
</p>
<Form
onSubmit={onSubmit}
decorators={[calculator]}
render={({ handleSubmit, reset, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<div>
<label>Minimum</label>
<Field
name="minimum"
component="input"
type="number"
placeholder="Minimum"
/>
</div>
<div>
<label>Maximum</label>
<Field
name="maximum"
component="input"
type="number"
placeholder="Maximum"
/>
</div>
<hr />
<div>
<label>Monday</label>
<Field
name="day[0]"
component="input"
type="number"
placeholder="Monday"
/>
</div>
<div>
<label>Tuesday</label>
<Field
name="day[1]"
component="input"
type="number"
placeholder="Tuesday"
/>
</div>
<div>
<label>Wednesday</label>
<Field
name="day[2]"
component="input"
type="number"
placeholder="Wednesday"
/>
</div>
<div>
<label>Thursday</label>
<Field
name="day[3]"
component="input"
type="number"
placeholder="Thursday"
/>
</div>
<div>
<label>Friday</label>
<Field
name="day[4]"
component="input"
type="number"
placeholder="Friday"
/>
</div>
<hr />
<div>
<label>Total</label>
<Field
name="total"
component="input"
type="number"
readOnly
placeholder="Total"
/>
</div>
<hr />
<div className="buttons">
<button type="submit" disabled={submitting}>
Submit
</button>
<button
type="button"
onClick={reset}
disabled={submitting || pristine}
>
Reset
</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
</Styles>
);
render(<App />, document.getElementById("root"));
================================================
FILE: examples/calculated-fields/package.json
================================================
{
"name": "react-final-form-calculated-fields",
"version": "1.0.0",
"description": "Demonstrates how to use the final-form-calculate decorator to achieve realtime field calculations.",
"keywords": [],
"homepage": "https://codesandbox.io/s/oq52p6v96y",
"main": "index.js",
"dependencies": {
"final-form": "4.20.4",
"final-form-calculate": "1.3.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-final-form": "6.5.3",
"styled-components": "4.2.0"
}
}
================================================
FILE: examples/calculated-fields/readme.md
================================================
# Calculated Fields
[](https://codesandbox.io/s/oq52p6v96y)
================================================
FILE: examples/chakra/index.js
================================================
/* eslint-disable jsx-a11y/accessible-emoji */
import React from "react";
import { render } from "react-dom";
import {
Box,
Button,
ButtonGroup,
CSSReset,
Heading,
Icon,
Link,
ThemeProvider,
theme,
FormControl,
FormLabel,
FormErrorMessage,
Input,
Checkbox,
Progress,
Radio,
RadioGroup,
Stack,
Textarea,
} from "@chakra-ui/core";
import { Form, Field, useField, useForm } from "react-final-form";
import validate from "./validate";
const sleep = (ms) => new Promise((resolve) =
gitextract_z4w9ccys/
├── .babelrc.js
├── .eslintrc
├── .flowconfig
├── .github/
│ ├── CODEOWNERS
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── ci.yml
│ └── lock.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .travis.yml
├── LICENSE
├── MIGRATION_V7.md
├── README.md
├── docs/
│ ├── api/
│ │ ├── Field.md
│ │ ├── Form.md
│ │ ├── FormSpy.md
│ │ ├── useField.md
│ │ ├── useForm.md
│ │ └── useFormState.md
│ ├── api.md
│ ├── examples/
│ │ ├── chakra.md
│ │ ├── field-level-validation.md
│ │ ├── record-level-validation.md
│ │ ├── simple.md
│ │ ├── submission-errors.md
│ │ ├── subscriptions.md
│ │ └── wizard.md
│ ├── examples.md
│ ├── faq.md
│ ├── getting-started.md
│ ├── migration/
│ │ ├── formik.md
│ │ └── redux-form.md
│ ├── philosophy.md
│ └── types/
│ ├── FieldProps.md
│ ├── FieldRenderProps.md
│ ├── FormProps.md
│ ├── FormRenderProps.md
│ ├── FormSpyProps.md
│ └── FormSpyRenderProps.md
├── eslint.config.mjs
├── examples/
│ ├── async-field-level-validation/
│ │ ├── Spinner.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── async-redux-submission/
│ │ ├── Styles.js
│ │ ├── asyncSubmissionMiddleware.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ ├── registrationDuck.js
│ │ └── store.js
│ ├── async-typeahead-redux/
│ │ ├── GithubUserTypeahead.jsx
│ │ ├── Styles.js
│ │ ├── actions.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ ├── store.js
│ │ └── useKeyword.js
│ ├── auto-save-field-blur/
│ │ ├── AutoSave.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── auto-save-selective-debounce/
│ │ ├── AutoSave.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── auto-save-with-debounce/
│ │ ├── AutoSave.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── calculated-fields/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── chakra/
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── validate.js
│ ├── conditional-fields/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── pickupTimes.js
│ │ └── readme.md
│ ├── credit-card/
│ │ ├── Card.js
│ │ ├── Styles.js
│ │ ├── cardUtils.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── sandbox.config.json
│ ├── custom-validation-engine/
│ │ ├── OnBlurValidation.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── debounced-record-level-validation/
│ │ ├── ErrorWithDelay.js
│ │ ├── README.md
│ │ ├── Styles.js
│ │ ├── index.js
│ │ └── package.json
│ ├── declarative-form-rules/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── downshift-typeahead/
│ │ ├── DownshiftInput.js
│ │ ├── Styles.js
│ │ ├── fruit.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── external-submit/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── field-arrays/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── field-level-validation/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── field-warnings/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── warning-engine.js
│ ├── fields-component/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── focus-first-error/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── validate.js
│ ├── format-on-blur/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── format-string-by-pattern/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── hybrid-sync-async-record-level-validation/
│ │ ├── Spinner.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── independent-error-component-render-props/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── independent-error-component-with-hooks/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── listening-for-external-changes/
│ │ ├── BooleanDecay.js
│ │ ├── ExternalModificationDetector.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── loading-initializing-values/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── loading-saving-reinitializing/
│ │ ├── LoadSaveReinitializeForm.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── material-ui/
│ │ ├── .prettierrc
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── parse-format/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── prefixed-fields/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── readme.md
│ ├── record-level-validation/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── redux/
│ │ ├── FormStateFromRedux.js
│ │ ├── FormStateToRedux.js
│ │ ├── Styles.js
│ │ ├── finalFormDuck.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── store.js
│ ├── reusable-field-groups/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── simple/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── strongly-typed-values-typescript/
│ │ ├── Styles.tsx
│ │ ├── components/
│ │ │ ├── CheckboxInput.tsx
│ │ │ ├── MultiCheckboxInput.tsx
│ │ │ ├── MultiSelectInput.tsx
│ │ │ ├── NumberInput.tsx
│ │ │ ├── RadioInput.tsx
│ │ │ ├── SelectInput.tsx
│ │ │ ├── TextAreaInput.tsx
│ │ │ └── TextInput.tsx
│ │ ├── index.tsx
│ │ └── readme.md
│ ├── styling-with-smooth-ui/
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── submission-errors/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── subscriptions/
│ │ ├── RenderCount.js
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── readme.md
│ ├── third-party-components/
│ │ ├── Styles.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── readme.md
│ │ └── states.js
│ └── wizard/
│ ├── Styles.js
│ ├── Wizard.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── package-scripts.js
├── package.json
├── rollup.config.mjs
├── src/
│ ├── Field.test.js
│ ├── Field.tsx
│ ├── FormSpy.test.js
│ ├── FormSpy.tsx
│ ├── ReactFinalForm.test.js
│ ├── ReactFinalForm.tsx
│ ├── context.test.js
│ ├── context.ts
│ ├── getValue.test.js
│ ├── getValue.ts
│ ├── getters.ts
│ ├── index.ts
│ ├── isReactNative.ts
│ ├── isSyntheticEvent.ts
│ ├── renderComponent.test.js
│ ├── renderComponent.ts
│ ├── shallowEqual.test.js
│ ├── shallowEqual.ts
│ ├── testUtils.ts
│ ├── types.ts
│ ├── useConstant.ts
│ ├── useConstantCallback.test.js
│ ├── useConstantCallback.ts
│ ├── useField.dynamic-name-869.test.js
│ ├── useField.test.js
│ ├── useField.ts
│ ├── useForm.test.js
│ ├── useForm.ts
│ ├── useFormState.test.js
│ ├── useFormState.ts
│ ├── useLatest.ts
│ └── useWhenValueChanges.ts
├── tsconfig.build.json
├── tsconfig.json
├── tslint.json
└── typescript/
├── Field.test.tsx
├── FormSpy.test.tsx
├── ReactFinalForm.test.tsx
├── index.d.ts
├── tsconfig.json
├── useField.test.tsx
└── useFormState.test.tsx
SYMBOL INDEX (131 symbols across 47 files)
FILE: examples/async-redux-submission/registrationDuck.js
constant REGISTER (line 4) | const REGISTER = "final-form-examples/registration/REGISTER";
constant REGISTER_SUCCESS (line 5) | const REGISTER_SUCCESS =
constant REGISTER_FAILURE (line 7) | const REGISTER_FAILURE =
function reducer (line 11) | function reducer(state = {}, action = {}) {
FILE: examples/auto-save-field-blur/AutoSave.js
class AutoSave (line 5) | class AutoSave extends React.Component {
method constructor (line 6) | constructor(props) {
method componentWillReceiveProps (line 11) | componentWillReceiveProps(nextProps) {
method render (line 38) | render() {
FILE: examples/auto-save-selective-debounce/AutoSave.js
class AutoSave (line 8) | class AutoSave extends React.Component {
method constructor (line 12) | constructor(props) {
method componentDidUpdate (line 21) | componentDidUpdate() {
method render (line 87) | render() {
FILE: examples/auto-save-with-debounce/AutoSave.js
class AutoSave (line 5) | class AutoSave extends React.Component {
method constructor (line 6) | constructor(props) {
method componentWillReceiveProps (line 11) | componentWillReceiveProps(nextProps) {
method render (line 36) | render() {
FILE: examples/credit-card/cardUtils.js
function clearNumber (line 3) | function clearNumber(value = "") {
function formatCreditCardNumber (line 7) | function formatCreditCardNumber(value) {
function formatCVC (line 40) | function formatCVC(value, prevValue, allValues = {}) {
function formatExpirationDate (line 52) | function formatExpirationDate(value) {
FILE: examples/custom-validation-engine/OnBlurValidation.js
class OnBlurValidation (line 5) | class OnBlurValidation extends React.Component {
method componentWillReceiveProps (line 11) | componentWillReceiveProps(nextProps) {
method render (line 46) | render() {
FILE: examples/listening-for-external-changes/BooleanDecay.js
class BooleanDecay (line 14) | class BooleanDecay extends React.Component {
method constructor (line 21) | constructor(props) {
method startTimer (line 28) | startTimer() {
method stopTimer (line 35) | stopTimer() {
method componentDidMount (line 41) | componentDidMount() {
method componentWillUnmount (line 47) | componentWillUnmount() {
method componentWillReceiveProps (line 51) | componentWillReceiveProps(nextProps) {
method render (line 63) | render() {
FILE: examples/listening-for-external-changes/ExternalModificationDetector.js
class ExternalModificationDetector (line 9) | class ExternalModificationDetector extends React.Component {
method constructor (line 10) | constructor(props) {
method componentWillReceiveProps (line 18) | componentWillReceiveProps(nextProps) {
method render (line 35) | render() {
FILE: examples/loading-initializing-values/index.js
class App (line 21) | class App extends React.Component {
method componentDidMount (line 23) | async componentDidMount() {
method render (line 29) | render() {
FILE: examples/loading-saving-reinitializing/LoadSaveReinitializeForm.js
class LoadSaveReinitializeForm (line 5) | class LoadSaveReinitializeForm extends React.Component {
method componentDidMount (line 49) | componentDidMount() {
method render (line 53) | render() {
FILE: examples/material-ui/index.js
function App (line 164) | function App() {
FILE: examples/redux/finalFormDuck.js
constant UPDATE_FORM_STATE (line 4) | const UPDATE_FORM_STATE =
function reducer (line 8) | function reducer(state = {}, action = {}) {
FILE: examples/strongly-typed-values-typescript/components/CheckboxInput.tsx
type Props (line 4) | type Props = FieldRenderProps<boolean, any>;
FILE: examples/strongly-typed-values-typescript/components/MultiCheckboxInput.tsx
type Props (line 4) | type Props = FieldRenderProps<string, any>;
FILE: examples/strongly-typed-values-typescript/components/MultiSelectInput.tsx
type Props (line 4) | type Props = FieldRenderProps<string[], any>;
FILE: examples/strongly-typed-values-typescript/components/NumberInput.tsx
type Props (line 4) | type Props = FieldRenderProps<number, any>;
FILE: examples/strongly-typed-values-typescript/components/RadioInput.tsx
function RadioInput (line 4) | function RadioInput<T extends string>({
FILE: examples/strongly-typed-values-typescript/components/SelectInput.tsx
type Props (line 4) | type Props = FieldRenderProps<string, any>;
FILE: examples/strongly-typed-values-typescript/components/TextAreaInput.tsx
type Props (line 4) | type Props = FieldRenderProps<string, any>;
FILE: examples/strongly-typed-values-typescript/components/TextInput.tsx
type Props (line 4) | type Props = FieldRenderProps<string, any>;
FILE: examples/strongly-typed-values-typescript/index.tsx
type Stooge (line 17) | type Stooge = "larry" | "moe" | "curly";
type Values (line 18) | interface Values {
FILE: examples/subscriptions/RenderCount.js
function RenderCount (line 4) | function RenderCount() {
FILE: examples/wizard/Wizard.js
class Wizard (line 5) | class Wizard extends React.Component {
method constructor (line 11) | constructor(props) {
method render (line 53) | render() {
FILE: src/Field.test.js
function sleep (line 11) | async function sleep(ms) {
FILE: src/Field.tsx
function FieldComponent (line 6) | function FieldComponent<
FILE: src/FormSpy.tsx
function FormSpy (line 7) | function FormSpy<FormValues = Record<string, any>>({
FILE: src/ReactFinalForm.test.js
function sleep (line 11) | async function sleep(ms) {
FILE: src/ReactFinalForm.tsx
function ReactFinalForm (line 40) | function ReactFinalForm<FormValues = Record<string, any>>({
FILE: src/index.ts
function withTypes (line 10) | function withTypes<FormValues = Record<string, any>>() {
FILE: src/renderComponent.test.js
method a (line 55) | get a() {
method b (line 59) | get b() {
FILE: src/renderComponent.ts
function renderComponent (line 6) | function renderComponent<T>(
FILE: src/shallowEqual.test.js
function Foo (line 48) | function Foo() {
FILE: src/testUtils.ts
function Toggle (line 11) | function Toggle({
type ErrorBoundaryProps (line 25) | interface ErrorBoundaryProps {
class ErrorBoundary (line 30) | class ErrorBoundary extends React.Component<ErrorBoundaryProps> {
method componentDidCatch (line 31) | componentDidCatch(error: Error) {
method render (line 35) | render() {
FILE: src/types.ts
type SupportedInputs (line 12) | type SupportedInputs = "input" | "select" | "textarea";
type ReactContext (line 14) | interface ReactContext<FormValues = Record<string, any>> {
type FieldInputProps (line 18) | interface FieldInputProps<
type FieldRenderProps (line 32) | interface FieldRenderProps<
type FieldMetaState (line 63) | type FieldMetaState<FieldValue = any> = FieldRenderProps<FieldValue>['me...
type SubmitEvent (line 65) | interface SubmitEvent {
type FormRenderProps (line 70) | interface FormRenderProps<FormValues = Record<string, any>>
type FormSpyRenderProps (line 78) | interface FormSpyRenderProps<FormValues = Record<string, any>>
type RenderableProps (line 83) | interface RenderableProps<T> {
type FormProps (line 89) | interface FormProps<FormValues = Record<string, any>>
type UseFieldAutoConfig (line 102) | interface UseFieldAutoConfig {
type UseFieldConfig (line 121) | interface UseFieldConfig extends UseFieldAutoConfig {
type FieldProps (line 125) | interface FieldProps<
type UseFormStateParams (line 137) | interface UseFormStateParams<FormValues = Record<string, any>> {
type FormSpyProps (line 142) | interface FormSpyProps<FormValues = Record<string, any>>
type FormSpyPropsWithForm (line 146) | interface FormSpyPropsWithForm<FormValues = Record<string, any>>
FILE: src/useConstant.ts
function useConstant (line 15) | function useConstant<T>(init: () => T): T {
FILE: src/useConstantCallback.ts
function useConstantCallback (line 9) | function useConstantCallback<T extends (...args: any[]) => any>(
FILE: src/useField.ts
function useField (line 30) | function useField<
FILE: src/useForm.ts
function useForm (line 5) | function useForm<FormValues = Record<string, any>>(
FILE: src/useFormState.ts
function useFormState (line 9) | function useFormState<FormValues = Record<string, any>>({
FILE: src/useLatest.ts
function useLatest (line 3) | function useLatest<T>(value: T): { readonly current: T } {
FILE: src/useWhenValueChanges.ts
function useWhenValueChanges (line 3) | function useWhenValueChanges(
FILE: typescript/Field.test.tsx
function FormText1 (line 6) | function FormText1({ input }: FieldRenderProps<string, HTMLInputElement>) {
function FieldNumberValue (line 13) | function FieldNumberValue() {
function FieldNumberInputValue (line 23) | function FieldNumberInputValue() {
FILE: typescript/FormSpy.test.tsx
function submitButtonSpy (line 4) | function submitButtonSpy() {
FILE: typescript/ReactFinalForm.test.tsx
function basic (line 22) | function basic() {
function simple (line 43) | function simple() {
function simpleSubscription (line 77) | function simpleSubscription() {
function mutated (line 113) | function mutated() {
type UserForm (line 146) | interface UserForm {
function withTypedFormData (line 157) | function withTypedFormData() {
function withTypedDecorator (line 185) | function withTypedDecorator() {
FILE: typescript/index.d.ts
type SupportedInputs (line 12) | type SupportedInputs = "input" | "select" | "textarea";
type ReactContext (line 14) | interface ReactContext<FormValues = Record<string, any>> {
type FieldInputProps (line 18) | interface FieldInputProps<
type FieldRenderProps (line 32) | interface FieldRenderProps<
type FieldMetaState (line 63) | type FieldMetaState<FieldValue = any> = FieldRenderProps<FieldValue>['me...
type SubmitEvent (line 65) | interface SubmitEvent {
type FormRenderProps (line 70) | interface FormRenderProps<FormValues = Record<string, any>>
type FormSpyRenderProps (line 78) | interface FormSpyRenderProps<FormValues = Record<string, any>>
type RenderableProps (line 83) | interface RenderableProps<T> {
type FormProps (line 89) | interface FormProps<FormValues = Record<string, any>>
type UseFieldAutoConfig (line 101) | interface UseFieldAutoConfig {
type UseFieldConfig (line 120) | interface UseFieldConfig extends UseFieldAutoConfig {
type FieldProps (line 124) | interface FieldProps<
type UseFormStateParams (line 136) | interface UseFormStateParams<FormValues = Record<string, any>> {
type FormSpyProps (line 141) | interface FormSpyProps<FormValues = Record<string, any>>
type FormSpyPropsWithForm (line 145) | interface FormSpyPropsWithForm<FormValues = Record<string, any>>
FILE: typescript/useField.test.tsx
function NumberFieldValue (line 6) | function NumberFieldValue() {
function NumberInputValue (line 11) | function NumberInputValue() {
function MyComponent (line 19) | function MyComponent() {
function MyTypedComponent (line 24) | function MyTypedComponent() {
function MyTypedComponentWithElement (line 29) | function MyTypedComponentWithElement() {
FILE: typescript/useFormState.test.tsx
function Comp1 (line 6) | function Comp1() {
function MyComponent (line 11) | function MyComponent() {
function MyTypedComponent (line 16) | function MyTypedComponent() {
Condensed preview — 293 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (822K chars).
[
{
"path": ".babelrc.js",
"chars": 486,
"preview": "const { NODE_ENV } = process.env;\nconst test = NODE_ENV === \"test\";\nconst loose = true;\n\nmodule.exports = {\n presets: ["
},
{
"path": ".eslintrc",
"chars": 239,
"preview": "{\n \"extends\": \"react-app\",\n \"plugins\": [\"react-hooks\"],\n \"rules\": {\n \"jsx-a11y/href-no-hash\": 0,\n \"react-hooks/"
},
{
"path": ".flowconfig",
"chars": 44,
"preview": "[ignore]\ndist\n\n[include]\n\n[libs]\n\n[options]\n"
},
{
"path": ".github/CODEOWNERS",
"chars": 11,
"preview": "* @erikras\n"
},
{
"path": ".github/CODE_OF_CONDUCT.md",
"chars": 3226,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": ".github/CONTRIBUTING.md",
"chars": 2594,
"preview": "# Contributing\n\nThanks for your interest in contributing to 🏁 React Final Form! Please take a\nmoment to review this docu"
},
{
"path": ".github/FUNDING.yml",
"chars": 545,
"preview": "# These are supported funding model platforms\n\ngithub: erikras\npatreon: erikras\nopen_collective: final-form\nko_fi: # Rep"
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 883,
"preview": "<!--\n\n👋 Hey, thanks for taking an interest in 🏁 React Final Form!\n\n-->\n\n### Are you submitting a **bug report** or a **f"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 822,
"preview": "<!--\n\n👋 Hey, thanks for your interest in contributing to 🏁 React Final Form!\n\n**Please ask first before starting work on"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1243,
"preview": "name: CI\n\non: [push]\n\njobs:\n lint:\n name: Lint\n runs-on: ubuntu-latest\n\n steps:\n - uses: actions/checkout"
},
{
"path": ".github/workflows/lock.yml",
"chars": 419,
"preview": "name: \"Lock Threads\"\n\non:\n schedule:\n - cron: \"0 * * * *\"\n workflow_dispatch:\n\npermissions:\n issues: write\n pull-"
},
{
"path": ".gitignore",
"chars": 114,
"preview": ".vscode\n*.iml\n.nyc_output\ncoverage\nflow-coverage\nnode_modules\ndist\nlib\nes\nnpm-debug.log\n.DS_Store\n.yalc\nyalc.lock\n"
},
{
"path": ".prettierignore",
"chars": 26,
"preview": "coverage\ndist\nnode_modules"
},
{
"path": ".prettierrc",
"chars": 29,
"preview": "{\n \"trailingComma\": \"all\"\n}\n"
},
{
"path": ".travis.yml",
"chars": 273,
"preview": "sudo: false\nlanguage: node_js\nbefore_install:\n - npm install -g npm@6.4.0\ncache:\n directories:\n - node_modules\nnoti"
},
{
"path": "LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2017 Erik Rasmussen\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "MIGRATION_V7.md",
"chars": 5116,
"preview": "# Migration Guide: react-final-form v6 → v7\n\n## Overview\n\nVersion 7.0.0 includes a complete TypeScript rewrite (migrated"
},
{
"path": "README.md",
"chars": 2665,
"preview": "# 🏁 React Final Form\n\n[](https://final-form.org/react)\n\n[. Li"
},
{
"path": "docs/api/Form.md",
"chars": 3202,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/Form). Lin"
},
{
"path": "docs/api/FormSpy.md",
"chars": 2637,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/FormSpy). "
},
{
"path": "docs/api/useField.md",
"chars": 871,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/useField)."
},
{
"path": "docs/api/useForm.md",
"chars": 589,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/useForm). "
},
{
"path": "docs/api/useFormState.md",
"chars": 532,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/useFormSta"
},
{
"path": "docs/api.md",
"chars": 1658,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api). Links ma"
},
{
"path": "docs/examples/chakra.md",
"chars": 461,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/chakr"
},
{
"path": "docs/examples/field-level-validation.md",
"chars": 557,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/field"
},
{
"path": "docs/examples/record-level-validation.md",
"chars": 544,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/recor"
},
{
"path": "docs/examples/simple.md",
"chars": 461,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/simpl"
},
{
"path": "docs/examples/submission-errors.md",
"chars": 611,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/submi"
},
{
"path": "docs/examples/subscriptions.md",
"chars": 703,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/subsc"
},
{
"path": "docs/examples/wizard.md",
"chars": 479,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/wizar"
},
{
"path": "docs/examples.md",
"chars": 12256,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples). Lin"
},
{
"path": "docs/faq.md",
"chars": 5552,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/faq). Links ma"
},
{
"path": "docs/getting-started.md",
"chars": 2519,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/getting-starte"
},
{
"path": "docs/migration/formik.md",
"chars": 8398,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/migration-guid"
},
{
"path": "docs/migration/redux-form.md",
"chars": 11857,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/migration-guid"
},
{
"path": "docs/philosophy.md",
"chars": 2429,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/philosophy). L"
},
{
"path": "docs/types/FieldProps.md",
"chars": 9949,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FieldPro"
},
{
"path": "docs/types/FieldRenderProps.md",
"chars": 6350,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FieldRen"
},
{
"path": "docs/types/FormProps.md",
"chars": 7745,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormProp"
},
{
"path": "docs/types/FormRenderProps.md",
"chars": 1127,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormRend"
},
{
"path": "docs/types/FormSpyProps.md",
"chars": 4072,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormSpyP"
},
{
"path": "docs/types/FormSpyRenderProps.md",
"chars": 706,
"preview": "# This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormSpyR"
},
{
"path": "eslint.config.mjs",
"chars": 5211,
"preview": "import js from \"@eslint/js\";\nimport typescriptParser from \"@typescript-eslint/parser\";\nimport typescriptPlugin from \"@ty"
},
{
"path": "examples/async-field-level-validation/Spinner.js",
"chars": 595,
"preview": "import styled, { keyframes } from \"styled-components\";\n\nconst rotation = keyframes`\n from {\n -webkit-transform: "
},
{
"path": "examples/async-field-level-validation/Styles.js",
"chars": 2477,
"preview": "import styled, { css } from \"styled-components\";\n\nconst btn = (light, dark) => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/async-field-level-validation/index.js",
"chars": 3448,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport Spinner from \"./Spi"
},
{
"path": "examples/async-field-level-validation/package.json",
"chars": 507,
"preview": "{\n \"name\": \"react-final-form-asynchronous-field-level-validation-example\",\n \"version\": \"1.0.0\",\n \"description\": \"This"
},
{
"path": "examples/async-field-level-validation/readme.md",
"chars": 196,
"preview": "# Asynchronous Field-Level Validation\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/async-redux-submission/asyncSubmissionMiddleware.js",
"chars": 950,
"preview": "import { REGISTER, REGISTER_SUCCESS } from \"./registrationDuck\";\n\nconst sleep = (ms) => new Promise((resolve) => setTime"
},
{
"path": "examples/async-redux-submission/index.js",
"chars": 4835,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport { Provider } from \"react-redux\";\nimport Styles fro"
},
{
"path": "examples/async-redux-submission/package.json",
"chars": 927,
"preview": "{\n \"name\": \"react-final-form-async-redux-submission\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to use r"
},
{
"path": "examples/async-redux-submission/readme.md",
"chars": 177,
"preview": "# Async Redux Submission\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/async-typeahead-redux/actions.js",
"chars": 470,
"preview": "export const requestGithubUsers = (query) => ({\n type: \"GITHUB_USERS_REQUEST\",\n query,\n});\n\nexport const storeGithubUs"
},
{
"path": "examples/async-typeahead-redux/index.js",
"chars": 1636,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport { Provider } from \"react-redux\";\nimport Styles fro"
},
{
"path": "examples/async-typeahead-redux/package.json",
"chars": 706,
"preview": "{\n \"name\": \"react-final-form-github-user-asynctypeahead\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how exte"
},
{
"path": "examples/async-typeahead-redux/readme.md",
"chars": 182,
"preview": "# Async Typeahead and Redux\n\n[ => {\n "
},
{
"path": "examples/auto-save-field-blur/AutoSave.js",
"chars": 1592,
"preview": "import React from \"react\";\nimport { FormSpy } from \"react-final-form\";\nimport diff from \"object-diff\";\n\nclass AutoSave e"
},
{
"path": "examples/auto-save-field-blur/Styles.js",
"chars": 3683,
"preview": "import styled, { css } from \"styled-components\";\n\nconst btn = (light, dark) => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/auto-save-field-blur/index.js",
"chars": 6016,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/auto-save-field-blur/package.json",
"chars": 497,
"preview": "{\n \"name\": \"react-final-form-auto-save-on-field-blur\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to crea"
},
{
"path": "examples/auto-save-field-blur/readme.md",
"chars": 176,
"preview": "# Auto-Save on Field Blur\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/auto-save-selective-debounce/index.js",
"chars": 5511,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/auto-save-selective-debounce/package.json",
"chars": 477,
"preview": "{\n \"name\": \"react-final-form-auto-save-with-selective-debounce\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates h"
},
{
"path": "examples/auto-save-selective-debounce/readme.md",
"chars": 196,
"preview": "# Auto-Save with _Selective_ Debounce\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/auto-save-with-debounce/index.js",
"chars": 5207,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/auto-save-with-debounce/package.json",
"chars": 515,
"preview": "{\n \"name\": \"react-final-form-auto-save-with-debounce\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to use "
},
{
"path": "examples/auto-save-with-debounce/readme.md",
"chars": 179,
"preview": "# Auto-Save with Debounce\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/calculated-fields/index.js",
"chars": 4337,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/calculated-fields/package.json",
"chars": 491,
"preview": "{\n \"name\": \"react-final-form-calculated-fields\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to use the fi"
},
{
"path": "examples/calculated-fields/readme.md",
"chars": 167,
"preview": "# Calculated Fields\n\n[](h"
},
{
"path": "examples/chakra/validate.js",
"chars": 799,
"preview": "const validate = (values) => {\n const errors = {};\n if (!values.firstName) {\n errors.firstName = \"Required\";\n }\n "
},
{
"path": "examples/conditional-fields/Styles.js",
"chars": 3954,
"preview": "import styled, { css } from \"styled-components\";\n\nconst btn = (light, dark) => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/conditional-fields/index.js",
"chars": 4878,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/conditional-fields/package.json",
"chars": 672,
"preview": "{\n \"name\": \"react-final-form-conditional-fields\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to condition"
},
{
"path": "examples/conditional-fields/pickupTimes.js",
"chars": 361,
"preview": "// This is not the best way to do this. Don't think to hard about this.\nconst now = new Date();\nlet hours = now.getHours"
},
{
"path": "examples/conditional-fields/readme.md",
"chars": 168,
"preview": "# Conditional Fields\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/credit-card/cardUtils.js",
"chars": 1368,
"preview": "import Payment from \"payment\";\n\nfunction clearNumber(value = \"\") {\n return value.replace(/\\D+/g, \"\");\n}\n\nexport functio"
},
{
"path": "examples/credit-card/index.js",
"chars": 3057,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/credit-card/package.json",
"chars": 802,
"preview": "{\n \"name\": \"react-final-form-credit-card-example\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to use 🏁 Re"
},
{
"path": "examples/credit-card/readme.md",
"chars": 163,
"preview": "# Credit Card Example\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/custom-validation-engine/index.js",
"chars": 4614,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/custom-validation-engine/package.json",
"chars": 572,
"preview": "{\n \"name\": \"react-final-form-custom-validation-engine\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how extens"
},
{
"path": "examples/custom-validation-engine/readme.md",
"chars": 181,
"preview": "# Custom Validation Engine\n\n[\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/downshift-typeahead/fruit.js",
"chars": 490,
"preview": "export default [\n { value: \"Apple\", label: \"🍎 Apple\" },\n { value: \"Banana\", label: \"🍌 Banana\" },\n { value: \"Cherry\", "
},
{
"path": "examples/downshift-typeahead/index.js",
"chars": 2664,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/downshift-typeahead/package.json",
"chars": 799,
"preview": "{\n \"name\": \"react-final-form-downshift-example\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to use a 🏎️ D"
},
{
"path": "examples/downshift-typeahead/readme.md",
"chars": 173,
"preview": "# Downshift Type-Ahead\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/external-submit/index.js",
"chars": 3761,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/external-submit/package.json",
"chars": 513,
"preview": "{\n \"name\": \"react-final-form-external-submit-button\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how you can "
},
{
"path": "examples/external-submit/readme.md",
"chars": 163,
"preview": "# External Submit\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/field-arrays/index.js",
"chars": 3070,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/field-arrays/package.json",
"chars": 495,
"preview": "{\n \"name\": \"react-final-form-field-arrays\",\n \"version\": \"1.0.0\",\n \"description\": \"This example demonstrates how to us"
},
{
"path": "examples/field-arrays/readme.md",
"chars": 157,
"preview": "# Field Arrays\n\n[]("
},
{
"path": "examples/field-level-validation/Styles.js",
"chars": 2428,
"preview": "import styled, { css } from \"styled-components\";\n\nconst btn = (light, dark) => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/field-level-validation/index.js",
"chars": 2762,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/field-level-validation/package.json",
"chars": 506,
"preview": "{\n \"name\": \"React Final Form - Synchronous Field Level Validation\",\n \"version\": \"1.0.0\",\n \"description\": \"An example "
},
{
"path": "examples/field-level-validation/readme.md",
"chars": 265,
"preview": "# Field Level Validation Example\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/field-warnings/index.js",
"chars": 2848,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { WarningEngine } f"
},
{
"path": "examples/field-warnings/package.json",
"chars": 465,
"preview": "{\n \"name\": \"react-final-form-warnings\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to use final-form-set-"
},
{
"path": "examples/field-warnings/readme.md",
"chars": 182,
"preview": "# Field Warnings\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/fields-component/index.js",
"chars": 2747,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/fields-component/package.json",
"chars": 577,
"preview": "{\n \"name\": \"react-final-form-fields-component\",\n \"version\": \"1.0.0\",\n \"description\": \"One common question from people"
},
{
"path": "examples/fields-component/readme.md",
"chars": 165,
"preview": "# Fields Component\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/focus-first-error/index.js",
"chars": 2941,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/focus-first-error/package.json",
"chars": 734,
"preview": "{\n \"name\": \"react-final-form-focus-on-first-error\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to use the"
},
{
"path": "examples/focus-first-error/readme.md",
"chars": 170,
"preview": "# Focus on First Error\n\n[ => {\n const errors = {};\n if (!values.firstName) {\n errors.firstName = \"Required\";\n }\n if"
},
{
"path": "examples/format-on-blur/Styles.js",
"chars": 4447,
"preview": "import styled, { css } from \"styled-components\";\n\nconst btn = (light, dark) => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/format-on-blur/index.js",
"chars": 2323,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/format-on-blur/package.json",
"chars": 773,
"preview": "{\n \"name\": \"react-final-form-format-on-blur-example\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to use t"
},
{
"path": "examples/format-on-blur/readme.md",
"chars": 161,
"preview": "# Format on Blur\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/format-string-by-pattern/index.js",
"chars": 1821,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/format-string-by-pattern/package.json",
"chars": 449,
"preview": "{\n \"name\": \"format-string-by-pattern-with-react-final-form\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how t"
},
{
"path": "examples/format-string-by-pattern/readme.md",
"chars": 180,
"preview": "# Format String by Pattern\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/hybrid-sync-async-record-level-validation/index.js",
"chars": 3336,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport Spinner from \"./Spi"
},
{
"path": "examples/hybrid-sync-async-record-level-validation/package.json",
"chars": 570,
"preview": "{\n \"name\": \"react-final-form-hybrid-synchronous-asynchronous-record-level-validation-example\",\n \"version\": \"1.0.0\",\n "
},
{
"path": "examples/hybrid-sync-async-record-level-validation/readme.md",
"chars": 222,
"preview": "# Hybrid Synchronous/Asynchronous Record-level Validation\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/loading-initializing-values/index.js",
"chars": 2412,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/loading-initializing-values/package.json",
"chars": 404,
"preview": "{\n \"name\": \"react-final-form-loading-and-initializing\",\n \"version\": \"1.0.0\",\n \"description\": \"This example demonstrat"
},
{
"path": "examples/loading-initializing-values/readme.md",
"chars": 191,
"preview": "# Loading and Initializing Values\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/loading-saving-reinitializing/index.js",
"chars": 4811,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Field } from \"rea"
},
{
"path": "examples/loading-saving-reinitializing/package.json",
"chars": 582,
"preview": "{\n \"name\": \"react-final-form-example-load-save-and-reinitialize\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates "
},
{
"path": "examples/loading-saving-reinitializing/readme.md",
"chars": 203,
"preview": "# Loading, Normalizing, Saving, and Reinitializing\n\n["
},
{
"path": "examples/parse-format/Styles.js",
"chars": 3236,
"preview": "import styled, { css } from \"styled-components\";\n\nconst btn = (light, dark) => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/parse-format/index.js",
"chars": 2264,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/parse-format/package.json",
"chars": 509,
"preview": "{\n \"name\": \"react-final-form-parse-and-format\",\n \"version\": \"1.0.0\",\n \"description\": \"Demonstrates how to use parse a"
},
{
"path": "examples/parse-format/readme.md",
"chars": 177,
"preview": "# Parse and Format (and Normalize)\n\n[ => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/prefixed-fields/index.js",
"chars": 3386,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/prefixed-fields/package.json",
"chars": 618,
"preview": "{\n \"name\": \"react-final-form-prefixed-fields\",\n \"version\": \"1.0.0\",\n \"description\": \"This example shows how to use Re"
},
{
"path": "examples/prefixed-fields/readme.md",
"chars": 191,
"preview": "# Prefixed Fields\n\n[\n"
},
{
"path": "examples/record-level-validation/Styles.js",
"chars": 2428,
"preview": "import styled, { css } from \"styled-components\";\n\nconst btn = (light, dark) => css`\n white-space: nowrap;\n display: in"
},
{
"path": "examples/record-level-validation/index.js",
"chars": 2638,
"preview": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Styles from \"./Styles\";\nimport { Form, Field } fro"
},
{
"path": "examples/record-level-validation/package.json",
"chars": 508,
"preview": "{\n \"name\": \"React Final Form - Synchronous Record Level Validation\",\n \"version\": \"1.0.0\",\n \"description\": \"An example"
},
{
"path": "examples/record-level-validation/readme.md",
"chars": 268,
"preview": "# Record Level Validation Example\n\n[
About this extraction
This page contains the full source code of the final-form/react-final-form GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 293 files (748.1 KB), approximately 233.0k tokens, and a symbol index with 131 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.