Skip to main content
Custom report fields allow you to capture additional information on expense reports beyond the standard fields. This guide explains how to create, edit, and manage report fields.

Understanding Report Fields

Report fields are custom data points that can be added to expense reports. They help capture organization-specific information needed for accounting, compliance, or reporting purposes.

Field Types

New Expensify supports several types of report fields:

Text Fields

Free-form text input for alphanumeric data

Dropdown Lists

Predefined list of options to choose from

Date Fields

Date picker for capturing dates

Formula Fields

Calculated fields based on other field values

Editing Report Fields

Field Editor Page

The Edit Report Field page provides a unified interface for editing all field types:
// From src/pages/EditReportFieldPage.tsx
function EditReportFieldPage({route}: EditReportFieldPageProps) {
    const {backTo, reportID, policyID} = route.params;
    const fieldKey = getReportFieldKey(route.params.fieldID);
    const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
    const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);
    const [recentlyUsedReportFields] = useOnyx(ONYXKEYS.RECENTLY_USED_REPORT_FIELDS);

    const isTitleField = route.params.fieldID === CONST.REPORT_FIELD_TITLE_FIELD_ID;
    let reportField = report?.fieldList?.[fieldKey] ?? policy?.fieldList?.[fieldKey];
    let policyField = policy?.fieldList?.[fieldKey] ?? reportField;

    // If the title field is missing, use fallback so that it can still be edited and matches the OldDot behavior.
    if (isTitleField && !reportField && !policyField) {
        const fallbackTitleField = getTitleFieldWithFallback(policy);
        reportField = fallbackTitleField;
        policyField = fallbackTitleField;
    }

    const isDisabled = isReportFieldDisabledForUser(report, reportField, policy) && reportField?.type !== CONST.REPORT_FIELD_TYPES.FORMULA;
    
    // ... field editing logic
}

Field Validation

Fields are validated before submission:
// From src/pages/EditReportFieldText.tsx
const validate = useCallback(
    (values: FormOnyxValues<typeof ONYXKEYS.FORMS.REPORT_FIELDS_EDIT_FORM>) => {
        const errors: FormInputErrors<typeof ONYXKEYS.FORMS.REPORT_FIELDS_EDIT_FORM> = {};
        const inputValue = values[fieldKey].trim();

        if (isRequired && inputValue === '') {
            errors[fieldKey] = translate('common.error.fieldRequired');
        }

        if (hasCircularReferences(inputValue, fieldName, fieldList)) {
            errors[fieldKey] = translate('workspace.reportFields.circularReferenceError');
        }

        return errors;
    },
    [fieldName, fieldKey, isRequired, translate, fieldList],
);

Text Fields

Editing Text Fields

Text fields provide a simple input for alphanumeric data:
// From src/pages/EditReportFieldText.tsx
function EditReportFieldTextPage({fieldName, onSubmit, fieldValue, isRequired, fieldKey, fieldList, disabled = false}: EditReportFieldTextPageProps) {
    const styles = useThemeStyles();
    const {translate} = useLocalize();
    const {inputCallbackRef} = useAutoFocusInput();
    const reportFieldName = Str.UCFirst(fieldName);

    return (
        <FormProvider
            style={[styles.flexGrow1, styles.ph5]}
            formID={ONYXKEYS.FORMS.REPORT_FIELDS_EDIT_FORM}
            onSubmit={onSubmit}
            validate={validate}
            submitButtonText={translate('common.save')}
            isSubmitButtonVisible={!disabled}
            enabledWhenOffline
            shouldHideFixErrorsAlert
        >
            <View style={styles.mb4}>
                <InputWrapper
                    InputComponent={TextInput}
                    inputID={fieldKey}
                    name={fieldKey}
                    defaultValue={fieldValue}
                    label={reportFieldName}
                    accessibilityLabel={reportFieldName}
                    role={CONST.ROLE.PRESENTATION}
                    ref={inputCallbackRef}
                    disabled={disabled}
                />
            </View>
        </FormProvider>
    );
}

Circular Reference Detection

Formula fields are checked for circular references:
// From src/pages/EditReportFieldText.tsx
if (hasCircularReferences(inputValue, fieldName, fieldList)) {
    errors[fieldKey] = translate('workspace.reportFields.circularReferenceError');
}
Circular ReferencesBe careful when creating formula fields that reference other fields. Circular references (Field A references Field B, which references Field A) will cause validation errors.

Editing Dropdown Fields

Dropdown fields provide a list of predefined options:
// From src/pages/EditReportFieldPage.tsx
{reportField.type === CONST.REPORT_FIELD_TYPES.LIST && (
    <EditReportFieldDropdown
        fieldKey={fieldKey}
        fieldValue={fieldValue}
        fieldOptions={policyField.values.filter((_value: string, index: number) => !policyField.disabledOptions.at(index))}
        onSubmit={handleReportFieldChange}
    />
)}
Only enabled options are displayed to users. Disabled options are filtered out from the selection list.

Date Fields

Editing Date Fields

Date fields use a date picker for easy date selection:
// From src/pages/EditReportFieldPage.tsx
{reportField.type === CONST.REPORT_FIELD_TYPES.DATE && (
    <EditReportFieldDate
        fieldName={Str.UCFirst(reportField.name)}
        fieldKey={fieldKey}
        fieldValue={fieldValue}
        isRequired={!reportField.deletable}
        onSubmit={handleReportFieldChange}
    />
)}

Formula Fields

Read-Only Formula Fields

Formula fields are calculated automatically and displayed as read-only:
// From src/pages/EditReportFieldPage.tsx
{reportField.type === CONST.REPORT_FIELD_TYPES.FORMULA && !isReportFieldTitle && (
    <EditReportFieldText
        fieldName={reportField.name}
        fieldKey={fieldKey}
        fieldValue={fieldValue}
        isRequired={!isReportFieldDeletable}
        onSubmit={handleReportFieldChange}
        fieldList={policy?.fieldList}
        disabled
    />
)}
Formula fields cannot be edited directly. They are automatically calculated based on the formula definition and values of other fields.

Updating Report Fields

Field Update Logic

When a report field is updated, the system performs validation and syncs with the server:
// From src/pages/EditReportFieldPage.tsx
const handleReportFieldChange = (form: FormOnyxValues<typeof ONYXKEYS.FORMS.REPORT_FIELDS_EDIT_FORM>) => {
    const value = form[fieldKey];
    if ((fieldValue ?? '').trim() === value?.trim()) {
        goBack();
        return;
    }

    if (isReportFieldTitle) {
        updateReportName(report.reportID, value, report.reportName ?? '');
        goBack();
    } else {
        if (value !== '') {
            updateReportField(
                {...report, reportID: report.reportID},
                {...reportField, value},
                reportField,
                policy as unknown as Policy,
                isASAPSubmitBetaEnabled,
                session?.accountID ?? CONST.DEFAULT_NUMBER_ID,
                session?.email ?? '',
                hasViolations,
                recentlyUsedReportFields,
                hasOtherViolations,
            );
        }
        goBack();
    }
};

Title Field Handling

The title field receives special handling:
// From src/pages/EditReportFieldPage.tsx
const fieldValue = isReportFieldTitle
    ? getReportNameFromReportNameUtils(report, reportAttributesByReportID) || (isPolicyFieldListEmpty(policy) ? CONST.REPORT.DEFAULT_EXPENSE_REPORT_NAME : '')
    : (reportField.value ?? reportField.defaultValue);

Deleting Report Fields

Field Deletion

Deletable fields can be removed from reports:
// From src/pages/EditReportFieldPage.tsx
const handleReportFieldDelete = async () => {
    const result = await showConfirmModal({
        title: translate('workspace.reportFields.delete'),
        prompt: translate('workspace.reportFields.deleteConfirmation'),
        confirmText: translate('common.delete'),
        cancelText: translate('common.cancel'),
        danger: true,
        shouldEnableNewFocusManagement: true,
    });
    if (result.action !== ModalActions.CONFIRM) {
        return;
    }
    Navigation.setNavigationActionToMicrotaskQueue(() => {
        goBack();
        setTimeout(() => {
            deleteReportField(report.reportID, reportField);
        }, CONST.ANIMATED_TRANSITION);
    });
};

const menuItems: PopoverMenuItem[] = [];

const isReportFieldDeletable = reportField.deletable && reportField?.fieldID !== CONST.REPORT_FIELD_TITLE_FIELD_ID;

if (isReportFieldDeletable) {
    menuItems.push({
        icon: icons.Trashcan,
        text: translate('common.delete'),
        onSelected: () => {
            handleReportFieldDelete();
        },
        shouldCallAfterModalHide: true,
    });
}
Field DeletionDeleting a report field removes it from the report permanently. This action cannot be undone. The title field cannot be deleted.

Field Permissions

Field Disabled States

Fields can be disabled based on user permissions and report state:
// From src/pages/EditReportFieldPage.tsx
const isDisabled = isReportFieldDisabledForUser(report, reportField, policy) && reportField?.type !== CONST.REPORT_FIELD_TYPES.FORMULA;

if (!reportFieldsEnabled || !reportField || !policyField || !report || isDisabled) {
    return (
        <ScreenWrapper
            includeSafeAreaPaddingBottom={false}
            shouldEnableMaxHeight
            testID="EditReportFieldPage"
        >
            <FullPageNotFoundView shouldShow />
        </ScreenWrapper>
    );
}

Required Fields

Required fields must be filled before report submission:
// From src/pages/EditReportFieldPage.tsx
const hasOtherViolations =
    report?.fieldList && Object.entries(report.fieldList).some(([key, field]) => key !== fieldKey && field.value === '' && !isReportFieldDisabled(report, reportField, policy));

Best Practices

Give fields clear, descriptive names that indicate what information should be entered (e.g., “Project Code” instead of “Code”).
When possible, provide sensible default values to reduce data entry effort.
Only mark fields as required if the information is absolutely necessary. Too many required fields can slow down the submission process.
Use dropdown fields instead of text fields when you need consistent values for reporting or categorization.
For formula fields, document the calculation logic clearly so users understand how values are derived.

Common Issues

Field Not EditableIf a field cannot be edited:
  • Check if the report is in a submitted or closed state
  • Verify you have permission to edit the report
  • Ensure the field is not a formula field (which are read-only)
  • Check if the field is disabled in the policy settings
Recently Used FieldsNew Expensify tracks recently used field values to provide quick suggestions for commonly entered data.

Field Visibility

Report Field Availability

Report fields are only available when:
// From src/pages/EditReportFieldPage.tsx
const isReportFieldTitle = isReportFieldOfTypeTitle(reportField);
const reportFieldsEnabled = ((isPaidGroupPolicyExpenseReport(report) || isInvoiceReport(report)) && !!policy?.areReportFieldsEnabled) || isReportFieldTitle;
  • The workspace has report fields enabled, OR
  • The report is a paid group policy expense report or invoice report, OR
  • The field is the title field (always available)

Reports Overview

Learn about expense reports

Creating Reports

Create and manage reports

Workspace Settings

Configure workspace field settings

Build docs developers (and LLMs) love