marmelab/react-admin · error · Error

Children of TranslatableInputs must have a source

Error message

Children of TranslatableInputs must have a source

What it means

TranslatableInputsTabContent builds a source context that suffixes each child input's source with the locale (e.g. 'description' becomes 'description.fr'). The getSource callback throws when a child reports an empty source, because there is nothing to namespace — every child of TranslatableInputs must declare a source.

Source

Thrown at packages/ra-ui-materialui/src/input/TranslatableInputsTabContent.tsx:45

    const props = useThemeProps({
        props: inProps,
        name: PREFIX,
    });
    const { children, groupKey = '', locale, ...other } = props;
    const { selectedLocale, getRecordForLocale } = useTranslatableContext();
    const parentSourceContext = useSourceContext();
    const record = useRecordContext(props);

    // The SourceContext will be read by children of TranslatableInputs to compute their composed source and label
    //
    // <TranslatableInputs locales={['en', 'fr']} /> => SourceContext is "fr"
    //     <TextInput source="description" /> => final source for this input will be "description.fr"
    // </TranslatableInputs>
    const sourceContext = useMemo(
        () => ({
            getSource: (source: string) => {
                if (!source) {
                    throw new Error(
                        'Children of TranslatableInputs must have a source'
                    );
                }
                return parentSourceContext.getSource(`${source}.${locale}`);
            },
            getLabel: (source: string) => {
                return parentSourceContext.getLabel(source);
            },
        }),
        [locale, parentSourceContext]
    );

    // As fields rely on the RecordContext to get their values and have no knowledge of the locale,
    // we need to create a new record with the values for the current locale only
    // Given the record { title: { en: 'title_en', fr: 'title_fr' } } and the locale 'fr',
    // the record for the locale 'fr' will be { title: 'title_fr' }
    const recordForLocale = useMemo(
        () => getRecordForLocale(record, locale),

View on GitHub (pinned to 051f511bb0)

Solutions

  1. Give every child input a source prop (e.g. <TextInput source="description" />).
  2. Move inputs that legitimately have no source outside of <TranslatableInputs>.
  3. For custom inputs, ensure they forward a non-empty source to the translatable context.

Example fix

// before
<TranslatableInputs locales={['en', 'fr']}>
  <SelectInput choices={[{ id: 1, name: 'One' }]} />
</TranslatableInputs>

// after
<TranslatableInputs locales={['en', 'fr']}>
  <SelectInput source="status" choices={[{ id: 1, name: 'One' }]} />
</TranslatableInputs>
Defensive patterns

Strategy: validation

Validate before calling

React.Children.forEach(children, child => {
  if (React.isValidElement(child) && !child.props.source) {
    throw new Error('Children of TranslatableInputs must have a source');
  }
});

Type guard

const hasSource = (c: React.ReactElement): c is React.ReactElement<{ source: string }> =>
  typeof (c.props as { source?: string }).source === 'string' &&
  (c.props as { source?: string }).source !== '';

Prevention

When it happens

Trigger: Placing an input without a source inside <TranslatableInputs> (e.g. a bare <SelectInput choices={...} />, a checkbox-only component, or a wrapper that strips source); rendering a component that calls getSource('') internally.

Common situations: Adding decorative/custom inputs inside TranslatableInputs; components that compute their source dynamically and yield an empty string; misusing TranslatableInputs around non-field components.

Related errors


AI-assisted analysis of marmelab/react-admin@051f511bb0 (2026-08-30). Data as JSON: /api/errors/dd743182429f94c7. Report an issue: GitHub.