Configuration¶
Quick start: shipped layout presets¶
The package ships several ready-made layout presets. The default is
COMMON_FIELDS_ALTERNATE_PAGED with the matching
FIELDS_BY_TYPE_ALTERNATE_PAGED — you don’t need to set anything to use it.
To pick a different preset, set both variables to the matching pair in your
instance invenio.cfg:
from invenio_modular_deposit_form.config.default import (
COMMON_FIELDS_DEFAULT_PAGED,
FIELDS_BY_TYPE_DEFAULT_PAGED,
)
MODULAR_DEPOSIT_FORM_COMMON_FIELDS = COMMON_FIELDS_DEFAULT_PAGED
MODULAR_DEPOSIT_FORM_FIELDS_BY_TYPE = FIELDS_BY_TYPE_DEFAULT_PAGED
Available presets:
|
Matching |
Layout shape |
|---|---|---|
|
|
Multi-page form with stepper, alternate page grouping suited to scholarly works. |
|
|
Multi-page form with the stepper inside the form header on mobile/tablet only and a sidebar page menu on larger screens. |
|
|
Same field layout as |
|
|
Single-page form (everything visible at once, like the stock InvenioRDM form). |
ALTERNATE_PAGED is in invenio_modular_deposit_form.config.alternate_paged;
the other three are in invenio_modular_deposit_form.config.default. To
customize one of the presets, copy it into your instance config and edit it
there — see Creating a custom form layout for
the schema.
Creating a custom form layout¶
You define the deposit form layout by setting
MODULAR_DEPOSIT_FORM_COMMON_FIELDS in your instance’s invenio.cfg. The
default configuration in this package’s config.py provides a good guide. At
its most basic, the layout configuration is a nested tree of dictionaries in
which each dictionary represents a React component. Some components are
structural layout components; others are wrappers for individual form field
components. Each dictionary in the tree has:
a unique
"section"ida
"component"property with a string corresponding to a component name incomponentsRegistry.jsan optional
"subsections"array of child componentsan optional readable
"label"
Note
The list of form layout components is extensible. The description below highlights the layout components built in by default.
Example: minimal two-page layout¶
# in invenio.cfg or your config module
MODULAR_DEPOSIT_FORM_COMMON_FIELDS = [
{
"section": "pages",
"component": "FormPages",
"subsections": [
{
"section": "1",
"label": "Type & Title",
"component": "FormPage",
"subsections": [
{
"section": "resource_type",
"label": "Resource Type",
"component": "ResourceTypeComponent",
},
{
"section": "titles",
"label": "Title",
"component": "TitlesComponent",
},
{
"section": "abstract",
"label": "Description",
"component": "AbstractComponent",
},
],
},
{
"section": "2",
"label": "Publish",
"subsections": [
{
"section": "submit_actions",
"label": "Publish",
"component": "SubmissionComponent",
},
],
},
],
}
]
Top-level form¶
The MODULAR_DEPOSIT_FORM_COMMON_FIELDS value is a list of layout objects.
Regions are identified by their "component" value; order in the array does
not matter.
You must include exactly one object with "component": "FormPages"; its
"subsections" array defines the form pages. You may also include optional
layout region objects to place content in the form header, left sidebar,
right sidebar, or form footer. Each of these is a sibling of the FormPages
object in the top-level list.
Required
FormPages — One object with
"component": "FormPages"and a"subsections"array. Defines the main form body (title, stepper, and page content). Optional responsive column keys:mobile,tablet,computer,largeScreen,widescreen(camelCase).
Optional layout regions
FormTitle —
"component": "FormTitle". Replaces the stock<h1>form heading area at the top of the form. When omitted, the form renders the default heading (record title plus selected community label, if any). When present, only the components in yoursubsectionsare rendered in that region — provide your own heading component(s).FormHeader —
"component": "FormHeader". Rendered above the form (below the community banner if shown), full width.FormLeftSidebar —
"component": "FormLeftSidebar". Rendered in a left column (default 3 grid units on computer, 16 on mobile). Optional responsive column keys:mobile,tablet,computer,largeScreen,widescreen.FormRightSidebar —
"component": "FormRightSidebar". Same as left sidebar. The default package layout uses it with FormFeedbackComponent (form feedback above; implementation paths and optionalhideMessageIconin field_components.md), SubmissionComponent (stock-style submit card), and AccessRightsComponent (Visibility). See Built-in field widget components for the difference between SubmissionComponent and HorizontalSubmissionComponent and when to use FormFeedbackComponent.FormFooter —
"component": "FormFooter". Rendered below the main form, full width.
FormPages accepts the same optional responsive column keys. If omitted, the main column at each breakpoint is 16 minus the left sidebar width minus the right sidebar width at that breakpoint.
Page navigation components (FormStepper, FormPageNavigationBar,
FormSidebarPageMenu) are in the component registry and appear when you include
them in the relevant region’s subsections. Pass classnames to control
visibility or styling. Use the same responsive visibility classes as
invenio-theme/invenio-app-rdm: mobile only, tablet only, computer only,
etc. Column width config keys use camelCase (largeScreen not
largeMonitor).
Example:
{
"section": "form-header",
"component": "FormHeader",
"subsections": [
{
"section": "header_message",
"label": "Notice",
"component": "SomeMessageComponent",
},
],
},
{
"section": "pages",
"component": "FormPages",
"subsections": [ ...pages... ],
},
Form page(s)¶
Each item in subsections is a page of the form with component value
"FormPage". The form may have just a single page; in that case all content is
visible at once, like the default InvenioRDM form.
If multiple pages are provided, include FormStepper in the FormHeader region
and optionally FormPageNavigationBar in the FormFooter and
FormSidebarPageMenu in the FormLeftSidebar. The label attributes of each
page dictionary are used in the stepper and sidebar menu.
Optional pages and resource types¶
The set of page ids is fixed in common FormPages subsections, but you
can reserve pages that only some resource types need by giving those pages
subsections: [] in the common layout (each page must still be a FormPage
entry with a stable section id and label for when it appears).
For the currently selected resource type, the form merges common subsections
with that type’s layout in MODULAR_DEPOSIT_FORM_FIELDS_BY_TYPE
(fields_by_type in the deposit config API). FormStepper,
FormSidebarPageMenu, footer FormPageNavigationBar (next/back), and the
main page area only include pages whose merged subsection list is
non-empty—after applying overrides and same_as references the same way as the
main form body. Types with no override for a placeholder page therefore do not
show that step; types that define subsections for that page id show it and use
the common label in navigation unless overridden.
Page layout components¶
Each member of a page’s subsections list is a top-level subdivision of that
page. Three kinds of section components are provided: FormSection, FormRow,
or a single field component.
FormSection¶
By default, a FormSection is rendered as a <fieldset> element. If a label
is provided, it is shown as the fieldset’s <legend> (controlled by
show_heading). The section is full-width and its contents are always visible.
Children are stacked vertically (full width). For horizontal layout of two
or more fields, put them inside a FormRow in the section’s subsections
(see FormRow below).
When collapsible is true, the section is rendered as a single accordion
pane: the label becomes a clickable header that expands or collapses the
content. Use this to keep long forms manageable. The initial state is controlled
by startExpanded.
Properties:
icon — FontAwesome icon name (semantic-ui icon set).
show_heading — When
false(default), the fieldset has no visible legend. When non-collapsible, set totrueto show the legend. When collapsible, the label is always shown in the accordion title.collapsible — When
true(defaultfalse), the section is an accordion pane.startExpanded — When
collapsibleistrue, set totrue(default) to show the section open initially, orfalseto show it collapsed.classnames — Optional CSS classes added to the container.
FormRow¶
A FormRow component renders a Semantic UI Form.Group. It holds one or more
child components in its subsections list, laid out in a horizontal row.
Spacing can be configured with classes like "equal-width".
Like FormSection, the FormRow dictionary may contain the basic component
properties and "classnames". Properties "label", "show_heading", and
"icon" are ignored. To get a fieldset with an overall legend, wrap the row in
a FormSection.
Wrapped field widget component¶
If a field widget component has "wrapped": True, it will automatically be
wrapped in its own FormSection. Such a wrapped field widget cannot have a
subsections property with children. For complex sections, use the other
structuring components or extend with a new composite field widget component.
Extending the layout components¶
To change the HTML of any structural component, override it using the
ReactOverridable mechanism and your instance’s mapping.js file. Ensure your
layout components pass through the children property. You can also create
entirely new custom layout components — see
Custom layout components in the
extending guide.
Field widget components¶
The lowest-level dictionaries represent individual form field widgets or
pre-structured sets. Each must have a "section" id and a "component" value
that exists in the combined component registry.
Most form field components can accept: description, helpText,
placeholder, icon, label, required (defaults to false unless
required in Yup schema or Invenio JSONSchema), classnames, showLabel
(defaults to true).
Any additional keys in the dictionary for a field widget will be passed through to the widget component as props. Props are overridden in the sequence: built-in defaults → React field_components definition → invenio.cfg values (lowest to highest priority).
Field widths¶
Within a FormRow, declare widths using the "classnames" property and Semantic
UI grid classes:
For equal-width fields, give the FormRow a
"classnames"value that includes"equal width".For different widths, give each field component a
"classnames"value that includes"X wide"(e.g."two wide"). You may use responsive width classes for different breakpoints.
Compound field components¶
You can provide compound field components that render a pre-configured block of multiple field components. In the layout you reference a single component that composes them internally.
Built-in example: CombinedDatesComponent — Renders PublicationDateComponent (publication date) and AdditionalDatesComponent (additional dates). In the layout add one section. Alternatively, use PublicationDateComponent and AdditionalDatesComponent as two separate sections if you want to place or configure them independently.
{
"section": "combined_dates",
"label": "Dates",
"component": "CombinedDatesComponent",
}
To author your own compound component, see Custom layout components and The componentsRegistry object in the extending guide. From the configuration side you only need to add one section that references its registered name, the same way you would for any other built-in component.
Responsive layout¶
The form layout supports two complementary mechanisms for varying what is shown by viewport width:
Per-breakpoint column widths — set widths on a layout region (
FormPages,FormLeftSidebar,FormRightSidebar) so the column collapses or grows at each breakpoint.CSS visibility classes — pass Semantic UI responsive helper classes via
classnames(for any region/component) ormenuItemClasses(for aFormPage’s stepper/sidebar entry) so the element is hidden viadisplay: noneat the breakpoints you choose.
Use widths to control how the grid lays out; use classes to hide individual elements without re-laying-out the columns.
Per-breakpoint column widths¶
The optional keys mobile, tablet, computer, largeScreen, and
widescreen (camelCase; use largeScreen, not largeMonitor) accept
Semantic UI grid column widths (1–16). They are accepted by FormPages,
FormLeftSidebar, and FormRightSidebar.
{
"component": "FormLeftSidebar",
"computer": 3,
"largeScreen": 3,
"widescreen": 3,
# mobile/tablet omitted: zero-width = collapsed at those breakpoints
"only": "large screen",
"subsections": [...],
},
The "only" key is a shortcut for zeroing widths at every breakpoint
smaller than the named one. "only": "computer" zeroes mobile and
tablet; "only": "large screen" zeroes mobile, tablet, and computer.
When you use "only", omit the now-redundant smaller-breakpoint keys (any value
you do supply is ignored — the zeroing wins).
If FormPages does not declare per-breakpoint widths, its width at each
breakpoint is 16 − <left sidebar width> − <right sidebar width> at that
breakpoint, so collapsing a sidebar via "only" automatically widens the main
column.
Aligning rows with SpacerColumn¶
SpacerColumn is an empty Grid.Column you drop into any region’s
subsections to consume grid units. Combine it with the per-breakpoint width
keys (mobile / tablet / computer / largeScreen / widescreen) and the
"only" shortcut to push other columns into alignment with sibling rows, add a
responsive-width margin, or reserve gutter space. It is most useful inside
FormHeader, FormTitle, FormFooter, and any FormRow where you need an
alignment other than the default left edge.
A SpacerColumn accepts the same width keys, the "only" shortcut, and the
same classnames (Semantic UI responsive helpers) as any other column. It takes
no subsections and renders as a single empty <div class="… column">.
Aligning a top stepper with the sidebars. When a page has a left and/or
right sidebar at large widths, a stepper placed in FormHeader needs leading
and trailing spacers so it begins at the same x-position as the main column and
ends where the right sidebar starts. The spacers’ widths mirror each sidebar’s
widths at each breakpoint:
_PAGED_FORM_HEADER_STEPPER_TOP = {
"component": "FormHeader",
"subsections": [
# Leading spacer: matches the (large-screen-only) left sidebar widths.
{
"component": "SpacerColumn",
"largeScreen": 1,
"widescreen": 2,
"only": "large screen",
},
# Stepper occupies the central space.
{
"component": "FormStepper",
"classnames": "column computer-only-strict",
"computer": 11,
},
# Trailing spacer: matches the right sidebar widths at computer+.
{
"component": "SpacerColumn",
"computer": 5,
"largeScreen": 4,
"widescreen": 4,
"only": "computer",
},
],
}
Responsive-width margin. Use a single leading spacer to push everything that
follows in the row by N grid units, with different N at each breakpoint, then
attach "only" so the margin disappears at smaller widths:
{"component": "SpacerColumn", "largeScreen": 1, "widescreen": 2, "only": "large screen"},
Aligning the form title with the main column. Place a FormTitle with a
leading SpacerColumn whose widths match the left sidebar so the heading starts
above the main column rather than the page edge:
{
"component": "FormTitle",
"subsections": [
{"component": "SpacerColumn", "largeScreen": 3, "widescreen": 3, "only": "large screen"},
{"component": "FormTitle", "computer": 16, "largeScreen": 13, "widescreen": 13},
],
},
Tips.
To hide a spacer without zeroing widths, use a Semantic UI visibility class via
classnames(e.g."mobile hidden"); to collapse it to zero width at smaller breakpoints, prefer"only". The two are interchangeable for hiding, but"only"keeps the breakpoint width math explicit.Always declare a width for every breakpoint the spacer should occupy. A column with no width set at a breakpoint inherits Semantic UI’s default (auto-fill of the row), which is usually not what you want for a spacer.
Spacers participate in the row’s 16-column budget. Make sure the sum of widths for each breakpoint adds up correctly across all columns in the row.
CSS visibility classes (classnames)¶
For finer-grained control — hiding a single component without affecting grid
widths — use Semantic UI’s responsive utility classes via classnames. The
element stays in the DOM; only its display is toggled.
Class |
Visible at |
|---|---|
|
Mobile only (≤767px). |
|
Tablet only (768–991px). |
|
Computer and larger (≥992px) — not strict “only computer”. |
|
Large screen and larger (≥1200px). |
|
Widescreen only (≥1920px). |
|
Everywhere except the named breakpoint. |
|
Only at the named breakpoints (composes the per-breakpoint hides). |
|
Computer breakpoint only (992–1199px) — hidden on mobile, tablet, large screen, and widescreen. Use when you need a strict “only computer” that excludes large monitors. |
computer-only-strict is defined in this package’s deposit_form.less; the
others are built into Semantic UI’s grid styles.
For components that render as a Grid.Column (FormStepper, custom
column-rendering components), include "column" in classnames so Semantic
UI’s column display rules apply alongside the visibility class:
{
"component": "FormStepper",
"classnames": "column tablet mobile only",
"mobile": 16,
"tablet": 16,
},
{
"component": "FormStepper",
"classnames": "column computer-only-strict",
"computer": 11,
},
Changing layout by resource type¶
The MODULAR_DEPOSIT_FORM_FIELDS_BY_TYPE config variable lets you change layout
elements based on the current resource type. Its keys are InvenioRDM resource
type names (from your instance’s resource_types vocabulary). The values are
dictionaries describing how pages should be laid out for that resource type;
keys must correspond to the section ids of the FormPage components in your
common fields layout.
If a page is not included in the resource type dictionary, the default layout from the common fields is used. If a page is included, the new page layout replaces the default for that page.
Example: replace the entire page whose section is "3" with a single ISBN
field when the selected resource type is "textDocument-book":
MODULAR_DEPOSIT_FORM_FIELDS_BY_TYPE = {
"textDocument-book": {
"3": {
"subsections": [
{
"section": "isbn",
"label": "ISBN",
"component": "ISBNComponent",
"wrapped": True,
},
],
},
},
}
Inheriting page layouts with same_as¶
To re-use another resource type’s page entry for the same FormPage section id,
set top-level same_as to that type’s id. The form resolves the target page
first (chasing further same_as references along the way, with cycle
detection), then shallow-merges your entry on top.
Merge rules:
Keys you set on your entry —
label,classnames, and any other keys the page object supports — override the resolved base.subsectionsis the only key with special handling: omit it and the base’s subsection list is inherited; include it and your list replaces the inherited one entirely (no item-level merging).The
same_askey itself is stripped from the merged result.Cycles (
a → b → a) are detected and resolve to the originating entry without recursion.
Example:
MODULAR_DEPOSIT_FORM_FIELDS_BY_TYPE = {
"textDocument-monograph": {
"2": {"same_as": "textDocument-book", "label": "Monograph details"},
},
}
Per-field modifications by resource type¶
Several config variables let you customize individual field widget props by resource type without declaring a whole new page layout. They all share the same shape:
{
"<resource-type-id>": {
"<dot-separated-metadata-path>": <value>,
...
},
...
}
Resource types not listed in the map fall back to the props the layout config
provides; field paths not listed for the current type are unaffected. The
FieldComponentWrapper reads each map at render time and overrides the matching
prop on the widget it wraps.
Display and metadata modifications (apply to all built-in widgets)¶
Variable |
Overrides |
Value type |
|---|---|---|
|
|
string (or |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
bool |
Example — change the title field’s icon by resource type:
MODULAR_DEPOSIT_FORM_ICON_MODIFICATIONS = {
"audio": {"metadata.title": "headphones"},
"dataset": {"metadata.title": "table"},
"image-photo": {"metadata.title": "camera"},
"presentation": {"metadata.title": "microphone"},
}
For custom fields, the field path uses the InvenioRDM custom-field syntax (e.g.
"custom_fields.journal:journal.title").
Field value modifications (only effective in custom widgets)¶
The following two variables are wired all the way through to each
FieldComponentWrapper, but none of the package’s built-in widgets currently
consume them. They are forwarded to the inner widget as the props named in the
table; a custom widget you write can read those props and use them.
Variable |
Prop received by widget |
Intended use |
|---|---|---|
|
|
Pre-fill an empty field with a default. |
|
|
Display value(s) the widget should surface as preferred suggestions. |
If you set these expecting them to affect the stock title/abstract/etc. widgets, they will not — see Adding your own components for how to write a widget that reads these props.
Other configuration flags¶
These flags toggle global form behaviour. None of them require asset rebuilds at
config-change time except MODULAR_DEPOSIT_FORM_USE_CLIENT_VALIDATION (see
note below).
MODULAR_DEPOSIT_FORM_USE_CLIENT_VALIDATION¶
Default: True.
When True, the form loads your validator.js (see Validation
and Adding your own components) and runs the
resulting Yup schema on every change. When False, no client schema is loaded;
field errors only appear after submit returns server-side validation errors.
Note
Changing this value requires rebuilding assets (invenio webpack build) — the choice is baked in at build time by webpack_extras.get_validator_path().
MODULAR_DEPOSIT_FORM_USE_CONFIRM_MODAL¶
Default: True.
When True, attempting to navigate to another page in a multi-page form while
the current page has unresolved errors opens a confirmation modal asking the
user to either fix the errors or proceed. When False, the errors are still
flagged on the leaving page but no modal interrupts navigation.
MODULAR_DEPOSIT_FORM_PRIORITY_RESOURCE_TYPES¶
Default:
("publication-article", "publication-peerreview", "publication-book", "publication-section", "lesson").
Ordered tuple of resource type ids that the ResourceTypeSelectorComponent
exposes as shortcut buttons (in addition to the always-present “Other…” control
that opens the full vocabulary select).
Important
Only the first five ids in the tuple are rendered as buttons. Additional ids are ignored. List your most-specific or highest-traffic types first.
Ids must come from your instance’s resource_types vocabulary.
MODULAR_DEPOSIT_FORM_PIDS_OVERRIDES¶
Default: a doi entry with KC-flavoured labels and help text (see
invenio_modular_deposit_form/config/config.py).
Maps PID scheme ids to dictionaries of UI-string overrides that are
shallow-merged into the matching entry of stock RDM_PERSISTENT_IDENTIFIERS
before the deposit form mounts. Any keys you set replace the corresponding keys
on the PID config; keys you omit keep their stock value.
Note
Only the doi scheme is currently honoured by the merge logic. Entries for other schemes are ignored.
Keys most commonly overridden:
Key |
Effect |
|---|---|
|
The visible field label (e.g. “Digital Object Identifier”). |
|
Input placeholder text. |
|
Label for the “reserve a PID” button. |
|
Label for the discard-reserved-PID button. |
|
Help text shown when the instance is configured to manage DOIs. |
|
Help text shown after a DOI has been reserved. |
|
Help text shown when DOIs are not managed by this instance. |
Example — switch the field label and reserve-button text:
MODULAR_DEPOSIT_FORM_PIDS_OVERRIDES = {
"doi": {
"field_label": "DOI",
"btn_label_get_pid": "Reserve a DOI",
},
}
Custom fields and namespaces¶
The package ships its own InvenioRDM custom-field defaults (codemeta, journal, imprint, meeting, thesis) and the matching UI definitions, applied to the standard Flask config keys:
RDM_NAMESPACES— namespace prefixes (codemeta,journal,imprint,meeting,thesis).RDM_CUSTOM_FIELDS— backend custom-field declarations (withVocabularyCFinstances replaced by a hardened subclass that won’t 500 the form when a vocabulary is missing — seeSafeVocabularyCF).RDM_CUSTOM_FIELDS_UI— UI definitions consumed byMODULAR_DEPOSIT_FORM_COMMON_FIELDSand the stock InvenioRDM custom-field rendering.
Important
Partial overrides are a footgun. The package applies these defaults in two passes:
init_configusessetdefault, so any value you set ininvenio.cfgwins outright.finalize_app(after the full config stack settles) re-applies the package defaults only when the value is still[]— i.e. nothing else, including yourinvenio.cfg, supplied a non-empty value.
If you set RDM_CUSTOM_FIELDS = [my_field] (or RDM_CUSTOM_FIELDS_UI = [my_ui]) in your invenio.cfg, you lose the package’s journal/imprint/codemeta/meeting/thesis fields entirely — neither pass will re-add them. To extend rather than replace, spread the package defaults yourself:
from invenio_modular_deposit_form.config import config as imdf_config
RDM_CUSTOM_FIELDS = [
*imdf_config.RDM_CUSTOM_FIELDS,
my_extra_field,
]
RDM_CUSTOM_FIELDS_UI = [
*imdf_config.RDM_CUSTOM_FIELDS_UI,
my_extra_ui,
]
RDM_NAMESPACES = {
**imdf_config.RDM_NAMESPACES,
"myns": "https://example.org/ns/",
}
InvenioRDM version 14 extensions¶
InvenioRDM v14 adds optional deposit form components that are not included in the default layout or component registry, so that this package builds and runs against invenio-app-rdm v13 (where those components do not exist).
If your instance runs InvenioRDM v14 and you want to enable them, you can register them via the component registry and add them to your layout.
Components
RecordDeletionComponent — Request deletion of a published record. Shown in the submit-actions region (e.g. next to the delete button) when the record is published. Requires backend config for record deletion (e.g.
config.record_deletionwithenabled) and vocabularyvocabularies.metadata.deletion_request_removal_reasons.FileModificationUntilComponent — Shows “Unlocked, X days to publish changes” in the Files section when
config.file_modificationis set. Place it as a sibling before the file uploader in the Files page content.
How to enable
Register the components — In your instance’s
componentsRegistry.js(the directory you expose via theinvenio_modular_deposit_form.components_registryentry point), import the wrappers from@js/invenio_modular_deposit_form/field_components/v14_componentsand add them to the registry asRecordDeletionComponentandFileModificationUntilComponent(value[RecordDeletionComponent, []]and[FileModificationUntilComponent, []]). This file is only loaded when your instance imports it, so the app-rdm v14 imports inside it do not affect the package build when building against v13.Add them to the layout — To show RecordDeletion in the submit area, add a section that references
RecordDeletionComponentin the same region asSubmissionComponent(e.g. inFormRightSidebarsubsections). To show FileModificationUntil in the Files section, add it as a subsection of the Files page (e.g. before or after the file upload section). The default package layout does not include these sections; copy the layout from the package’sconfig.pyand add the v14 sections where you want them.
See Adding your own React components for how to register the
components_registry entry point.