Form Config Map
The first argument to fork/form is a config
map. Every key is optional except :on-submit
if you want submission handling. Here's the full set:
| Prop | Type | Default | Description |
|---|---|---|---|
:initial-values | map | — | Pre-populates form fields. Keys must match input :name attributes. e.g. {"name" "John", "email" ""} |
:initial-touched | map | — | Pre-populates fields AND marks them as touched. Useful for edit forms where you want validation errors visible immediately. |
:path | keyword | vector | — | Where to store global state in re-frame app-db. Can be a keyword (:form) or a vector ([:forms :login]). Only relevant for fork.re-frame. |
:form-id | string | — | HTML id for the form element. If omitted, a random id is generated and available via the :form-id key in props. |
:keywordize-keys | boolean | — | When true, field names are treated as keywords instead of strings. Use normalize-name to convert keyword input names for the DOM. |
:prevent-default? | boolean | — | Prevents the browser's default form submission behavior. Almost always true for SPA forms. |
:clean-on-unmount? | boolean | — | Resets the re-frame app-db state at :path when the component unmounts. Only applies to fork.re-frame. |
:validation | function | — | A function that receives the values map and returns errors. Any validation library works. Fork blocks submission while errors exist. |
:on-submit | function | — | Called on valid submission. Receives a map with :state, :path, :values, :dirty, and :reset. |
:component-did-mount | function | — | Lifecycle hook called after mount. Receives a map of handlers: set-touched, set-untouched, set-values, disable, enable, disabled?, handle-change, handle-blur, send-server-request. |
:state | ratom | — | Provide your own ratom for form state. Useful when you need to access form state from outside the form component. |
:props | map | — | Pass arbitrary data through to your form component. Accessible via the :props key in the component's argument map. |
Reagent vs Re-frame
The APIs are identical. The difference is where server-related state lives:
;; Reagent: everything in a local ratom
(ns app.core
(:require [fork.reagent :as fork]))
;; State helpers operate on the ratom directly
(swap! state fork/set-submitting path true)
;; Re-frame: server state lives in app-db
(ns app.core
(:require [fork.re-frame :as fork]))
;; State helpers operate on the db inside events
(rf/reg-event-db
:my-event
(fn [db [_ path]]
(fork/set-submitting db path true)))Keyword Keys
By default, Fork uses string keys for field names. If you prefer
keywords (including namespaced keywords), enable
:keywordize-keys and use
normalize-name for DOM attributes:
[fork/form {:keywordize-keys true
:initial-values {:user/name "John"
:user/email ""}}
(fn [{:keys [values handle-change handle-blur normalize-name]}]
[:div
[:input
{:name (normalize-name :user/name)
:value (values :user/name)
:on-change handle-change
:on-blur handle-blur}]
[:input
{:name (normalize-name :user/email)
:value (values :user/email)
:on-change handle-change
:on-blur handle-blur}]])] normalize-name converts :user/name
to "user/name" for the DOM name
attribute. When :keywordize-keys is not set, it
returns the value unchanged.
Custom State
Pass your own ratom via :state to access
form state from outside the form component. Fork merges its initial
state into your atom, preserving any existing keys:
(def my-state (r/atom {:custom-key "preserved"}))
[fork/form {:state my-state
:initial-values {"name" ""}}
(fn [{:keys [values handle-change]}]
;; my-state now contains both :custom-key and Fork's :values, :touched, etc.
[:input
{:name "name"
:value (values "name")
:on-change handle-change}])]
;; Outside the form, you can read:
(:values @my-state) ;; => {"name" "current-value"}Passing Props
Use :props to pass data through to your form
component without polluting the config:
(defn my-form
[{:keys [props values handle-change handle-blur]}]
[:div
[:h2 (:title props)]
[:input
{:name "input"
:value (values "input")
:on-change handle-change
:on-blur handle-blur}]])
(defn app []
[fork/form {:props {:title "Registration Form"}
:initial-values {"input" ""}}
my-form])Next Steps
- Validation — pluggable validation with any library
- Field Arrays — dynamic field groups
- API Reference — every handler and option