Deposit Form Components
This page documents the form components available for customizing record deposit forms. For search result components, see Search result components.
Field Metadata
Field metadata (labels, hints, required status) is defined directly in your model YAML files, not passed as props manually. During model compilation, metadata is extracted and made available through React hooks.
When creating custom field components, use the useFieldData hook:
import { TextField as InvenioTextField } from "react-invenio-forms";
import { useFieldData } from "@js/oarepo_ui/forms";
export const CustomField = ({ fieldPath, icon, ...rest }) => {
const { getFieldData } = useFieldData();
return (
<InvenioTextField
optimized
fieldPath={fieldPath}
{...getFieldData({ fieldPath, icon })}
{...rest}
/>
);
};Important: Rules of Hooks
The content returned by getFieldData is memoized. Call this function only at the top level of your component to avoid breaking React’s rules of hooks:
✅ Good - At component top level:
const MyForm = () => {
const { getFieldData } = useFieldData();
const field1Metadata = getFieldData({ fieldPath: "metadata.name" });
const field2Metadata = getFieldData({ fieldPath: "metadata.value" });
return /* ... */;
};❌ Bad - Inside loops or conditionals:
{items.map((item) => (
<Field key={item.id} metadata={getFieldData({ fieldPath: item.fieldPath })} />
))}Available Input Components
Basic Text Input
import { TextField } from "@js/oarepo_ui/forms";
<TextField fieldPath="metadata.title" />| Prop | Type | Default | Description |
|---|---|---|---|
fieldPath | string | (required) | Path to the field in dot notation |
fieldRepresentation | string | ”full" | "full”, “compact”, or “text” |
icon | string | "" | Icon to display |
...rest | any | - | Passed to InvenioTextField |
Automatically applies field metadata, XSS protection, and Formik integration.
String Array Input
For fields that contain an array of strings:
import { StringArrayField } from "@js/oarepo_ui/forms";
<StringArrayField fieldPath="metadata.tags" />| Prop | Type | Default | Description |
|---|---|---|---|
fieldPath | string | (required) | Path to the field |
label | string|node | (from model) | Custom label |
addButtonLabel | string|node | ”Add” | Button label text |
helpText | string | "" | Help/instruction text |
defaultNewValue | string | "" | Default value for new items |
required | boolean | (from model) | Required status |
showEmptyValue | boolean | false | Show empty value on mount |
fieldRepresentation | string | ”text” | Field representation style |
icon | string | (from model) | Icon to display |
Rich Text Editor
Uses TinyMCE for formatted text input:
import { RichInputField } from "react-invenio-forms";
import { OarepoRichEditor } from "@js/oarepo_ui/forms";
<RichInputField
fieldPath="metadata.description"
label="Description"
editor={<OarepoRichEditor />}
/>Note: Unlike most components, RichInputField requires explicit field metadata props.
Date Pickers (EDTF)
Support for Extended Date/Time Format with precision options:
import {
EDTFSingleDatePickerField,
EDTFDateRangePickerField,
} from "@js/oarepo_ui/forms";
// Single date
<EDTFSingleDatePickerField fieldPath="metadata.publicationDate" />
// Date range
<EDTFDateRangePickerField fieldPath="metadata.eventDateRange" />Common props (both components):
| Prop | Type | Default | Description |
|---|---|---|---|
fieldPath | string | (required) | Path to the field |
label | string|node | (from model) | Custom label |
icon | string | ”calendar” | Icon to display |
required | boolean | (from model) | Required status |
helpText | string | (from model) | Help text to display |
EDTFSingleDatePickerField specific props:
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder | string | (from model) | Placeholder text |
datePickerProps | object | Props for underlying date picker | |
customInputProps | object | Props for custom input element |
EDTFDateRangePickerField specific props:
| Prop | Type | Default | Description |
|---|---|---|---|
dateRangeInputPlaceholder | string | ”Choose date range (From - To).” | Placeholder for range mode |
singleDateInputPlaceholder | string | ”Choose one date.” | Placeholder for single date mode |
datePickerPropsOverrides | object | Override picker behavior |
Multilingual Inputs
For content in multiple languages:
import {
I18nTextInputField,
I18nRichInputField,
} from "@js/oarepo_ui/forms";
// Plain text
<I18nTextInputField fieldPath="metadata.multilingualTitle" />
// Rich text
<I18nRichInputField fieldPath="metadata.multilingualDescription" />Data format:
{
"lang": "en",
"value": "Title in English"
}Common props (both components):
| Prop | Type | Default | Description |
|---|---|---|---|
fieldPath | string | (required) | Path to the field |
lngFieldWidth | number | 3 | Language selector width (1-16) |
optimized | boolean | true | Optimized rendering |
usedLanguages | array | [] | Languages already in use |
I18nRichInputField additional props:
| Prop | Type | Default | Description |
|---|---|---|---|
editorConfig | object | Configuration for rich text editor |
Vocabulary Selection
For controlled terminology dropdowns:
import {
LocalVocabularySelectField,
VocabularySelectField,
} from "@js/oarepo_vocabularies/form";
// Pre-loaded local vocabulary (from formConfig)
<LocalVocabularySelectField
fieldPath="metadata.resourceType"
vocabularyName="resource-types"
/>
// API-loaded vocabulary
<VocabularySelectField
fieldPath="metadata.subjects"
vocabularyName="subjects"
multiple={true}
/>Common props (both components):
| Prop | Type | Default | Description |
|---|---|---|---|
fieldPath | string | (required) | Path to the field |
vocabularyName | string | (required) | Vocabulary name |
multiple | boolean | (from model) | Allow multiple selections |
clearable | boolean | true | Allow clearing selection |
showLeafsOnly | boolean | false | Show only leaf nodes in hierarchies |
filterFunction | func | (opt) => opt | Filter function for options |
icon | string | ”tag” / (from model) | Icon to display |
LocalVocabularySelectField - Best for small vocabularies loaded all at once:
| Prop | Type | Default | Description |
|---|---|---|---|
optimized | boolean | true | Optimized rendering |
usedOptions | array | [] | Options already in use elsewhere |
VocabularySelectField - For large vocabularies loaded via API:
| Prop | Type | Default | Description |
|---|---|---|---|
externalAuthority | boolean | false | External authority mode |
suggestionAPIHeaders | object | ”see above” | API headers for suggestions |
Complex Nested Fields
For arrays of nested fields:
import React from "react";
import { ArrayField, GroupField } from "react-invenio-forms";
import { LocalVocabularySelectField } from "@js/oarepo_vocabularies/form";
import {
TextField,
StringArrayField,
EDTFDateRangePickerField,
} from "@js/oarepo_ui/forms";
import { useFieldData } from "@js/oarepo_ui/forms";
export const EventsField = ({ fieldPath }) => {
const { getFieldData } = useFieldData();
return (
<ArrayField
addButtonLabel="Add event"
fieldPath={fieldPath}
{...getFieldData({ fieldPath, fieldRepresentation: "text" })}
>
{({ indexPath }) => {
const prefix = `${fieldPath}.${indexPath}`;
return (
<>
<TextField fieldPath={`${prefix}.eventName`} />
<EDTFDateRangePickerField fieldPath={`${prefix}.eventDate`} />
<GroupField>
<TextField fieldPath={`${prefix}.location.place`} />
<LocalVocabularySelectField
fieldPath={`${prefix}.location.country`}
vocabularyName="countries"
/>
</GroupField>
</>
);
}}
</ArrayField>
);
};Fields in Modal
For very complex fields, consider using a modal for better UX. This requires an internal Formik instance to manage the modal form.
Example: Creatibutors field from RDM.
Trade-offs:
- Use modals only when truly necessary
- Requires data conversion between main form and modal
- Can be challenging with deeply nested data