Skip to main content

Step Handler

Advanced

Concept

The Step Handler provides powerful hooks that execute after each step of the import process, giving you complete control over the data flow and user experience. Unlike its predecessor (Data Handler) which runs before the header and the review steps, the Step Handler runs after each step is completed, allowing you to validate, modify, or block progression based on your requirements.

Step Handler Workflow Diagram
DescriptionThe Step Handler is an advanced feature that provides hooks after each step of the import process. It enables you to access the raw uploaded and parsed files, validate data, add cell-specific errors/warnings/infos, and control the import flow with customizable alert and block modals. With the Step Handler , you can implement complex validation logic, throw pre-submission errors, access column and option mappings, and provide custom user feedback at any stage of the import process. Step Handler replaces the Data Handler and provides more granular control with hooks for upload, header selection, mapping, and review steps.
Available hooks
  • uploadStep() - Runs after file upload and parsing
  • headerStep() - Runs after header selection confirmation
  • mappingStep() - Runs after mapping confirmation
  • reviewStep() - Runs when user clicks "Complete import" in the review step
Control functions
  • alert() - Shows a customizable warning modal with proceed/cancel options
  • block() - Shows a customizable error modal that blocks progression
  • updateData() - Updates the data for the next step

Basic syntax

Here is an example of the Step Handler's syntax:

stepHandler={{
uploadStep: async ({parsedData, rawData, updateData, alert, block}) => {
// Hook runs after file upload and parsing
},
headerStep: async ({data, updateData, alert, block}) => {
// Hook runs after header selection confirmation
},
mappingStep: async ({data, tdm, updateData, alert, block, logs}) => {
// Hook runs after mapping confirmation
},
reviewStep: async ({data, updateData, block, logs}) => {
// Hook runs when user clicks "Complete import" in the review step
}
}}

uploadStep()

DescriptionThis hook runs after the user has uploaded one or multiple files and the files have been parsed. It provides access to both the raw uploaded files and their parsed data, allowing validation and transformation before proceeding to the next step.
Parameters
  • parsedData - Array of parsed file objects with data, fileName, fileSize, fileType, and sheetName properties
  • rawData - Array of raw/unparsed data for each uploaded file
  • updateData - Function to update the data structure for the next step
  • alert - Function to show a warning modal with proceed/cancel options
  • block - Function to show an error modal that blocks progression
Run eventThe uploadStep function is executed immediately after file upload and parsing is complete

Parameters

parsedData

Array of sheet objects containing the parsed data and metadata for each uploaded file/sheet.

Sheet object properties:

  • data: A 2D array representing the content of the sheet
  • fileName: The name of the uploaded file
  • fileSize: The size of the file in megabytes (MB)
  • fileType: The file extension (e.g., "xlsx", "csv", "xls")
  • sheetName: The name of the sheet (for Excel files) or defaults to the fileName for single-sheet files

Example:

[
{
"data": [
[
"company",
"order_id",
"customer_email",
"order_instructions",
], ...
],
"fileName": "test-file.xlsx",
"fileSize": 0.0067,
"fileType": "xlsx",
"sheetName": "test-sheet-1"
}, ...
]

rawData

Array of raw file objects containing the unprocessed files exactly as they were uploaded. This gives you access to the original files before any parsing or processing.


updateData

Function to modify the data structure before proceeding to the next step.

Return formats:

  • Single sheet: Return data as a 2D array or array of objects

    updateData([
    ["id", "name"],
    ["12345", "Jason"],
    ]);
  • Multiple sheets: Return an array of sheet objects

    updateData([
    {
    fileName: "Customers",
    sheetName: "Internal", // optional
    data: [
    // supports 2D array and array of row objects
    ["id", "name"],
    ["12345", "Jason"],
    ],
    },
    {
    fileName: "Products",
    data: [
    // supports 2D array and array of row objects
    { id: "12", quantity: 24 },
    { id: "13", quantity: 88 },
    ],
    },
    ]);

alert

Function to show a warning modal with proceed/cancel options.

Parameters:

  • title: Title of the warning modal
  • description: Description text explaining the warning
  • cancelButton: Text for the cancel button
  • proceedButton: Text for the proceed button

block

Function to show an error modal that blocks progression.

Parameters:

  • title: Text for the modal title
  • description: Text explaining the error
  • closeButton: Text for the close button

Implementation Example

stepHandler={{
uploadStep: async ({parsedData, rawData, updateData, alert, block}) => {
// Access raw uploaded files
console.log('Raw files:', rawData);

// Process parsed data
const processedData = parsedData.map(file => ({
fileName: file.fileName,
sheetName: file.sheetName,
data: file.data.filter(row => row.length > 0) // Remove empty rows
}));

// Check for issues and potentially block
if (processedData.some(file => file.data.length === 0)) {
block({
title: "Empty File Detected",
description: "One or more files contain no data. Please upload valid files.",
closeButton: "Close"
});
return;
}

// Update data for next step
updateData(processedData);
}
}}

Interaction with other features

  • allowManualInput: When the user presses the "Manual Entry" button, uploadStep() is not triggered, and the user is forwarded to the review step.
  • automaticHeaderDetection: When automaticHeaderDetection === true, it does not affect uploadStep(). uploadStep() is triggered after parsing. If it finishes without alert() or block() being called, the user is forwarded to the next step (sheet or header selection).
  • Contextual Engine: uploadStep() runs after file upload, before the Contextual Engine is applied or the modal to use the Contextual Engine is displayed.

headerStep()

DescriptionThis hook runs after the user has confirmed their header selection. It allows validation of the selected headers and provides the ability to modify the data structure before proceeding to either the mapping step or join step.
Parameters
  • data - Array of sheet objects with selected headers
  • updateData - Function to update data structure
  • alert - Function to show a warning modal
  • block - Function to show an error modal
Run eventThe headerStep function is executed when the user confirms their header selection

Parameters

data

Array of sheet objects containing the data after header selection. It can contain one or multiple sheet objects depending on how many sheets have been selected inside the sheet selection. Each sheet object has the same structure as uploadStep's parsedData.

Sheet object properties:

  • data: A 2D array representing the content of the sheet, where the first row contains the headers
  • fileName: The name of the uploaded file
  • fileSize: The size of the file in megabytes (MB)
  • fileType: The file extension (e.g., "xlsx", "csv", "xls")
  • sheetName: The name of the sheet (for Excel files) or defaults to the fileName for single-sheet files

Example:

[
{
"data": [
[
"company",
"order_id",
"customer_email",
"order_instructions",
], ...
],
"fileName": "test-file.xlsx",
"fileSize": 0.0067,
"fileType": "xlsx",
"sheetName": "test-sheet-1"
}, ...
]

updateData

Function to modify the data structure before proceeding to the next step.

Return formats:

  • Single 2D array: Forwards the user to the mapping step
  • Single array of row objects: Forwards the user to the mapping step
  • Array with single sheet object: Forwards the user to the mapping step
  • Array with multiple sheet objects: Forwards the user to the join step when multipleFileUpload is activated

Example:

// Single 2D array
updateData([
["id", "name"],
["12345", "Jason"],
]);

// Single array of row objects
updateData([
{ id: "12", quantity: 24 },
{ id: "13", quantity: 88 },
]);

// Array with single sheet object
updateData([
{
fileName: "Sheet1",
data: [
["header1", "header2"],
["value1", "value2"],
],
},
]);

// Array with multiple sheet objects
updateData([
{
fileName: "Sheet1",
data: [
["header1", "header2"],
["value1", "value2"],
],
},
{
fileName: "Sheet2",
data: [
["header3", "header4"],
["value3", "value4"],
],
},
]);

alert

Function to show a warning modal with proceed/cancel options.

Parameters:

  • title: Title of the warning modal
  • description: Description text explaining the warning
  • cancelButton: Text for the cancel button
  • proceedButton: Text for the proceed button

block

Function to show an error modal that blocks progression.

Parameters:

  • title: Text for the modal title
  • description: Text explaining the error
  • closeButton: Text for the close button

Implementation example

headerStep: async ({ data, updateData, alert, block }) => {
// Validate required headers
const requiredHeaders = ["id", "email", "name"];
const headers = data[0].data[0];
const missingHeaders = requiredHeaders.filter((h) => !headers.includes(h));

if (missingHeaders.length > 0) {
alert({
title: "Missing required headers",
description: `The following headers are required: ${missingHeaders.join(", ")}`,
cancelButton: "Fix headers",
proceedButton: "Continue anyway",
});
}

// Remove empty columns
const cleanData = data.map((sheet) => ({
...sheet,
data: sheet.data.map((row) => row.filter((_, colIndex) => sheet.data.some((r) => r[colIndex]?.trim()))),
}));

updateData(cleanData);
};

Interaction with other features

  • automaticHeaderDetection: When the user presses the "Manual Entry" button, headerStep() is not triggered, and the user is forwarded to the review step.
  • multipleFileUpload: If multiple sheets are returned via updateData() but multiple file upload is not activated, only the first sheet object is used for the mapping step.
  • allowManualInput: When automaticHeaderDetection === true, headerStep() is triggered at the end of the automated header selection. If block() or alert() is called, the corresponding modal is shown. Closing the modal returns the user to the previous step. Continuing forwards the user to the next step.
  • Contextual Engine: If the Contextual Engine is triggered in the header selection step (single sheet import), headerStep() does not run. If the Contextual Engine is triggered in the join step (multiple sheets import), headerStep() runs at the end of header selection/before going to the join step.

mappingStep()

DescriptionThis hook runs when the user confirms their column mappings and all required mappings are complete. It provides access to the mapped data and the Target Data Model (TDM), allowing validation and modification of both the schema and data.
Parameters
  • data - Array of row objects containing the TDM keys
  • tdm - Target Data Model object with schema modification methods
  • updateData - Function to update data with validation messages
  • alert - Function to show a warning modal
  • block - Function to show an error modal
  • logs - Object containing details about the column and option mappings, the custom-added columns and options
Run eventThe mappingStep function is executed after the user confirms their column mappings and all required mappings are complete

Parameters

data

Array of row objects containing the mapped values using the Target Data Model (TDM) keys.

Example:

[
{
id: "12345",
name: "Jason Miller",
email: "[email protected]"
}, ...
]

tdm

Target Data Model object for modifying the schema.

Available methods:

  • addColumn(): Add a new column to the schema
    tdm.addColumn({
    key: "column_key",
    label: "column_label",
    columnType: "string",
    validations: [{ validate: "required" }],
    hidden: false,
    disabled: false,
    });
  • removeColumn(): Remove a column from the schema via its key
    tdm.removeColumn("column_key");

updateData

Function to update data and adding cell-specific error/warning/info messages.

Data formats:

  • Simple data: Array of row objects without validation messages
    updateData([
    { id: "12345", name: "Jason" },
    { id: "67890", name: "Max" },
    ]);
  • Data with validation: Array of row objects with value and info properties
    updateData([
    {
    id: {
    value: "12345",
    info: [
    {
    message: "Invalid ID format",
    level: "error",
    },
    ],
    },
    },
    ]);

alert

Function to show a warning modal with proceed/cancel options.

Parameters:

  • title: Title of the warning modal
  • description: Description text explaining the warning
  • cancelButton: Text for the cancel button
  • proceedButton: Text for the proceed button

block

Function to show an error modal that blocks progression.

Parameters:

  • title: Text for the modal title
  • description: Text explaining the error
  • closeButton: Text for the close button

logs

Object containing details about the column and option mappings, the custom-added columns and options during the mapping step.

Properties:

  • mappings: Array of mapping objects containing relation between input and target columns, including option mappings for category columns
  • columns: Object containing added columns and options via allowCustomColumns and allowCustomOptions
    Advanced

Example:

{
"mappings": [
{
"sourceColumn": "Company Identification Number", // input column
"targetColumn": "company_code" // matched Target Data Model column
},
{
"sourceColumn": "Status",
"targetColumn": "deal_status",
"options": [ // when this is a category column (columnType === "category", "currency_code", "country_code_alpha_2" or "country_code_alpha_3"
{
"sourceValue": "In progress", // input value
"targetOptions": ["Ongoing", ...] // matched Target Data Model dropdown option (can contain multiple strings when isMultiSelect === true)
},
{
"sourceValue": "TBD",
"targetOptions": [] // when being unmatched
}
]
},
{
"sourceColumn": "",
"targetColumn": "address"
}, ...
],
"columns": {
"addedColumns": [
{
"label": "Revenue",
"key": "revenue",
"columnType": "currency_eur"
}
],
"addedOptions": [
{
"columnKey": "deal_status",
"dropdownOptions": [
{
"label": "Done",
"value": "done",
"type": "string"
}
]
}
]
}
}

Implementation example

mappingStep: async ({ data, tdm, updateData, alert, block, logs }) => {
// Check mapping completeness
const unmappedRequired = logs.mappings.filter((m) => !m.sourceColumn).map((m) => m.targetColumn);

if (unmappedRequired.length > 0) {
block({
title: "Unmapped Columns",
description: `Every column needs to be mapped. Please map the following columns: ${unmappedRequired.join(", ")}`,
closeButton: "Fix mappings",
});
return;
}

// Add custom validation column
tdm.addColumn({
key: "status",
label: "Validation status",
columnType: "string",
hidden: true,
});

// Validate email format
const validatedData = data.map((row) => {
const validatedRow = { ...row };

if (row.email && !row.email.includes("@")) {
validatedRow.email = {
value: row.email,
info: [
{
message: "Invalid email format",
level: "error",
},
],
};
validatedRow.status = "Invalid";
}

return validatedRow;
});

updateData(validatedData);
};

Interaction with other features

  • Cleaning Functions: mappingStep() is executed before column hooks and onEntryInit(). columnHooks() receives the data returned by mappingStep() (without errors, warnings, infos). If alert() or block() is called, columnHooks() and onEntryInit() are not triggered.
  • automaticMapping: mappingStep() is called even if the mapping step is skipped via automatic mapping. If block() or alert() are used, the mapping step is not skipped and the corresponding alert/block modal is shown.
  • onlyMappedColumns: Normally, row objects in data contain all TDM keys. If onlyMappedColumns === true, only mapped TDM columns are included.
  • Contextual Engine: If the Contextual Engine runs and is successful, mappingStep() is triggered with the output of the Contextual Engine.
  • Hidden & disabled: Hidden and disabled columns are accessible and updatable via data and updateData in mappingStep(). Errors on hidden columns are ignored.

reviewStep()

DescriptionThis hook runs when the user attempts to complete their import from the review step. It serves as the final validation point before submission, allowing pre-submission checks and data enrichment.
Parameters
  • data - Array of row objects with validation messages
  • updateData - Function to update the data
  • block - Function to show an error modal
  • logs - Object containing details about the mappings, the custom-added columns and options
Run eventThe reviewStep function is executed when the user clicks "Complete import" in the review step

Parameters

data

Array of row objects containing the data, shown in the review step, including all error/warning/info messages. If the cell has no extra message, info is undefined.

Example:

[
{
id: {
value: "12345",
info: [{
message: "ID verified",
level: "info"
}]
},
email: {
value: "@getnuvo.com",
info: [{
message: "Invalid email format",
level: "error"
}]
},
amount: {
value: 36000,
info: [{
message: "This value seems to extraordinary high. Please check again.",
level: "warning"
}]
},
name: {
value: "Jason Miller",
info: undefined
},
...
}, ...
]

updateData

Function to update the final data before submission. You can modify the values and add error/warning/info messages cell-specific.

Example:

// Add an error message to every name cell
updateData(
data.map((row) => ({
...row,
name: {
value: row.name,
info: [
{
message: "Invalid name",
level: "error",
},
],
},
})),
);

block

Function to show an error modal that blocks progression.

Parameters:

  • title: Text for the modal title
  • description: Text explaining the error
  • closeButton: Text for the close button
info

Note that alert is not available in reviewStep()


logs

Object containing details about the column and option mappings, the custom-added columns and options during the entire import process.

Properties:

  • mappings: Array of mapping objects containing relation between input and target columns, including option mappings for category columns
  • columns: Object containing added columns and options via allowCustomColumns and allowCustomOptions
    Advanced

Example:

{
"mappings": [
{
"sourceColumn": "Company Identification Number", // input column
"targetColumn": "company_code" // matched Target Data Model column
},
{
"sourceColumn": "Status",
"targetColumn": "deal_status",
"options": [ // when this is a category column (columnType === "category", "currency_code", "country_code_alpha_2" or "country_code_alpha_3"
{
"sourceValue": "In progress", // input value
"targetOptions": ["Ongoing", ...] // matched Target Data Model dropdown option (can contain multiple strings when isMultiSelect === true)
},
{
"sourceValue": "TBD",
"targetOptions": [] // when being unmatched
}
]
},
{
"sourceColumn": "",
"targetColumn": "address"
}, ...
],
"columns": {
"addedColumns": [
{
"label": "Revenue",
"key": "revenue",
"columnType": "currency_eur"
}
],
"addedOptions": [
{
"columnKey": "deal_status",
"dropdownOptions": [
{
"label": "Done",
"value": "done",
"type": "string"
}
]
}
]
}
}

Implementation example

stepHandler={{
reviewStep: async ({data, updateData, block, logs}) => {
// Pre-submission validation
const errorRows = data.filter(row =>
Object.values(row).some(cell =>
cell?.info?.some(info => info.level === 'error')
)
);

if (errorRows.length > 0) {
block({
title: "Data Validation Failed",
description: `${errorRows.length} rows contain errors that must be fixed before submission.`,
closeButton: "Fix Errors"
});
return;
}

// Add submission timestamp
const timestampedData = data.map(row => ({
...row,
submitted_at: new Date().toISOString()
}));

updateData(timestampedData);
}
}}

Interaction with other features

  • onlyMappedColumns: Normally, data contains all TDM keys, even if empty or null. If onlyMappedColumns === true, only mapped TDM columns are included.
  • Hidden & disabled: Hidden and disabled columns are accessible and updatable via data and updateData in reviewStep(). Errors on hidden columns are ignored.

Control functions

alert()

Description:

Shows a warning modal with "Continue" and "Cancel" options. Clicking "Cancel" or the "X" button closes the modal and keeps the user on the current step. Clicking "Continue" allows the user to proceed to the next step.

Alert modal:

Default Alert Modal

Parameters:

  • title: Title of the modal (default: "Are you sure you want to continue?")
  • description: Description text (default: "Some required steps may not be completed yet.")
  • cancelButton: Text for cancel button (default: "Cancel")
  • proceedButton: Text for proceed button (default: "Continue")

Example:

alert({
title: "Warning Title",
description: "Warning description text",
cancelButton: "Cancel",
proceedButton: "Continue",
});

Callback availability:

CallbackAvailablility
CallbackAvailablility
uploadStep()Yes
headerStep()Yes
mappingStep()Yes
reviewStep()No

block()

Description:

Shows an error modal that prevents the user from proceeding. Clicking the "Close" or "X" button closes the modal and returns the user to the current step.

Block modal:

Default Block Modal

Parameters:

  • title: Title of the modal (default: "Unable to continue")
  • description: Description text (default: "Something went wrong. Please try again.")
  • closeButton: Text for close button (default: "Close")

Example:

block({
title: "Error Title",
description: "Error description text",
closeButton: "Close",
});

Callback availability:

CallbackAvailablility
CallbackAvailablility
uploadStep()Yes
headerStep()Yes
mappingStep()Yes
reviewStep()Yes

Implementation examples

<NuvoImporter
licenseKey="Your License Key"
settings={{
developerMode: true,
identifier: "product_data",
columns: [
{
key: "id",
label: "Product ID",
columnType: "string",
validations: [{ validate: "required" }],
},
{
key: "name",
label: "Product Name",
columnType: "string",
validations: [{ validate: "required" }],
},
{
key: "price",
label: "Price",
columnType: "number",
},
{
key: "email",
label: "Contact Email",
columnType: "string",
},
],
}}
stepHandler={{
uploadStep: async ({ parsedData, rawData, updateData, alert, block }) => {
// Hook runs after file upload and parsing
},
headerStep: async ({ data, updateData, alert, block }) => {
// Hook runs after header selection confirmation
},
mappingStep: async ({ data, tdm, updateData, alert, block, logs }) => {
// Hook runs after mapping confirmation
},
reviewStep: async ({ data, updateData, block, logs }) => {
// Hook runs when user clicks "Complete import" in the review step
},
}}
onResults={(results, errors, complete, logs, block) => {
console.log("Import completed:", results);
complete();
}}
/>

Interaction with Cleaning Functions

The Step Handler can be used together with our Cleaning Functions to validate and transform data after the mapping step. mappingStep() runs first, followed by columnHooks(), which operates on the data returned from mappingStep(). Finally, onEntryInit() executes using the data as modified by both of the preceding functions.

Here is an overview of all import steps and hooks, including those from the Step Handler, the Cleaning Functions, and onResults().

Entire Workflow Diagram

Migration from Data Handler

The Step Handler replaces the Data Handler with improved functionality.

Here is a quick overview of the main differences and similarities:

Data HandlerStep HandlerDescription
headerStep()uploadStep()Runs after file upload
reviewStep()mappingStep()Runs after mapping confirmation
N/AheaderStep()New hook for accessing/modifying data after header selection
N/AreviewStep()New hook for pre-submission validation
N/Ablock()Blocks user from progression under certain conditions
N/Aalert()Shows custom alert modal under certain conditions
N/ARaw file accessAvailable in uploadStep()
N/ABlocking user during pre-submissionAvailable in reviewStep() via block()

Migration steps:

  1. Replace dataHandler with stepHandler
  2. Move dataHandler.headerStep() logic to stepHandler.uploadStep()
  3. Move dataHandler.reviewStep() logic to stepHandler.mappingStep()
  4. Update data access patterns for new parameter structure & return mechanism
  5. Optionally: Add new stepHandler.headerStep() to access and modify data after the header selection and stepHandler.reviewStep() for pre-submission validation
Need help migrating?

We offer two options to help with your migration:


For more examples and advanced use cases, visit our knowledge base in our User Platform.