npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@adobe-commerce/aio-experience-kit

v1.0.4

Published

A comprehensive TypeScript toolkit for building Adobe Commerce Admin UI extensions with React Spectrum components. Provides production-ready components and utilities for Adobe I/O extensibility platform integration.

Readme

@adobe-commerce/aio-experience-kit

Overview

A TypeScript toolkit for building Adobe Commerce Admin UI extensions with React Spectrum components. This library provides production-ready components and utilities that integrate seamlessly with Adobe's I/O extensibility platform.

Key Benefits:

  • Rapid Development: Pre-built components reduce development time from weeks to days
  • Adobe Design Consistency: All components follow Adobe Spectrum design principles
  • Enterprise Ready: Built-in accessibility, internationalization, and performance optimizations
  • Developer Experience: Full TypeScript support with intelligent code completion
  • Extensible Architecture: Modular design allows for easy customization and extension
  • Production Tested: Battle-tested components used in real Adobe Commerce extensions

Installation

Install the TypeScript library with custom React components using React Spectrum:

npm install @adobe-commerce/aio-experience-kit

Usage

The toolkit is organized into two main modules:

⚛️ React Components

A collection of specialized React components built on Adobe React Spectrum, designed for creating consistent and accessible admin interfaces.

MainContainer

The main layout component for Adobe Commerce admin extensions. Provides flexible layouts with integrated navigation, routing, and responsive design.

import { MainContainer, LayoutOptions, NavigationOptions } from '@adobe-commerce/aio-experience-kit';
import { Text } from '@adobe/react-spectrum';
import HomeIcon from "@spectrum-icons/workflow/Home"
import BookIcon from "@spectrum-icons/workflow/Book"

const LibraryApp = (props) => {
  const navigationButtons = [
    {
      label: "Home",
      path: "/",
      icon: <HomeIcon size={"S"} gridArea="Home" />
    }, {
      label: "Books",
      path: "/book/grid",
      icon: <BookIcon size={"S"} gridArea="Books" />
    }
  ];

  const appRoutes = [
    {
      paths: ['/'],
      component: <Text>Home</Text>
    }, {
      paths: ['/book/:component/', '/book/:component/:id'],
      component: <Text>Books</Text>
    }
  ];

  return (
    <MainContainer
      title="Library Application"
      titleLevel={1}
      buttons={navigationButtons}
      routes={appRoutes}
      navigation={NavigationOptions.NavigationButtons}
      layout={LayoutOptions.OneColumn}
    />
  );
};

Features:

  • Flexible Layouts: Choose between OneColumn for focused content or TwoColumnLeft for navigation-heavy interfaces
  • Smart Navigation: Integrated navigation system supporting both link-based and button-based patterns
  • Routing Integration: Seamless React Router integration with automatic route matching and component rendering
  • Responsive Design: Automatically adapts to different screen sizes while maintaining usability
  • Customizable Spacing: Fine-grained control over padding, margins, and component spacing
  • Accessibility First: Built-in keyboard navigation, screen reader support, and focus management

DataTable

A powerful data grid component for displaying and managing tabular data with advanced features like sorting, selection, and bulk operations.

import React, { useState } from 'react';
import { View, ActionButton, Text } from '@adobe/react-spectrum';
import { DataTable, useRouteParams } from '@adobe-commerce/aio-experience-kit';
import AddCircle from '@spectrum-icons/workflow/AddCircle';

const EntityGrid = ({ actionCallHeaders }) => {
  const { getNavigate } = useRouteParams();
  const navigate = getNavigate();

  const [isProcessing, setProcessing] = useState(false);
  const [gridData, setGridData] = useState([]);
  
  const entityModel = new EntityModel(actionCallHeaders);
  const columns = entityModel.columns();

  const buttons = [
    <ActionButton
      variant="primary"
      type="button"
      marginEnd="size-150"
      isDisabled={isProcessing}
      onPress={() => {
        navigate('/entity/form');
      }}
    >
      <AddCircle gridArea="Create Entity" />
      <Text>Create Entity</Text>
    </ActionButton>
  ];

  const gridActions = [
    { key: 'edit', text: 'Edit' },
    { key: 'delete', text: 'Delete' }
  ];

  const massActions = [
    { key: 'delete', text: 'Delete' }
  ];

  return (
    <DataTable
      columns={columns}
      data={gridData}
      buttons={buttons}
      gridActions={gridActions}
      massActions={massActions}
      isProcessing={isProcessing}
      onMassActionPress={async (key, ids) => {
        switch (key) {
          case 'delete':
            // Process mass delete
            break;
        }
      }}
      onGridActionPress={async (key, item) => {
        switch (key) {
          case 'edit':
            navigate(`/entity/form/${item.id}`);
            break;
          case 'delete':
            // Process single delete
            break;
        }
      }}
      onGridLoad={async () => {
        setGridData(await entityModel.list());
      }}
    />
  );
};

// Entity Model
class EntityModel {
  constructor(actionCallHeaders) {
    this.actionCallHeaders = actionCallHeaders;
  }

  async list() {
    // Load data using API
    return [];
  }

  columns() {
    return [
      { name: 'Name', uid: 'name' },
      { name: 'Status', uid: 'status' },
      { name: 'Created At', uid: 'created_at' },
      { name: 'Updated At', uid: 'updated_at' },
      { name: 'Actions', uid: 'actions' }
    ];
  }
}

Vertical Scrolling for Large Datasets (v1.0.3+):

Control table height with automatic scrolling:

<DataTable
  columns={columns}
  data={largeDataset}
  maxHeight="size-6000" // Spectrum token (480px)
  // or maxHeight="500px"  // CSS value
  // or maxHeight="60vh"   // Viewport height
  onGridLoad={async () => {
    // Load large dataset
  }}
/>

Search and Filtering Capabilities:

Add built-in search functionality to enable users to quickly find and filter records within your DataTable. The search bar is configurable and supports both button-triggered and search-as-you-type modes.

Example 1: Basic Search (Button Click Mode)

<DataTable
  columns={columns}
  data={data}
  searchConfig={{
    enabled: true,
    placeholder: "Search products...",
    searchOnType: false, // Only search when button clicked or Enter pressed
  }}
  onSearch={async (searchText) => {
    // Triggered only on button click or Enter key
    await fetchFilteredData(searchText);
  }}
/>

Example 2: Search-as-you-type with Debounce (Recommended for Client-Side Filtering)

const [allData, setAllData] = useState(products);
const [filteredData, setFilteredData] = useState(products);

<DataTable
  columns={columns}
  data={filteredData} // Show filtered results
  searchConfig={{
    enabled: true,
    searchOnType: true, // Search while typing
    debounceMs: 300, // Wait 300ms after user stops typing
    showClearButton: true,
    minCharacters: 2, // Only search if 2+ characters entered
  }}
  onSearch={async (searchText) => {
    if (!searchText) {
      setFilteredData(allData); // Show all if search is empty
      return;
    }
    
    // Client-side filtering
    const filtered = allData.filter(item =>
      Object.values(item).some(val =>
        String(val).toLowerCase().includes(searchText.toLowerCase())
      )
    );
    setFilteredData(filtered);
  }}
/>

Example 3: Server-Side Search with Loading State

const [isSearching, setSearching] = useState(false);

<DataTable
  columns={columns}
  data={data}
  searchConfig={{
    enabled: true,
    isSearching: isSearching, // Shows spinner in search button
    placeholder: "Search across 10,000+ records...",
    searchOnType: true,
    debounceMs: 800, // Longer debounce for server calls
  }}
  onSearch={async (searchText) => {
    setSearching(true);
    try {
      const results = await apiSearchCall(searchText);
      setData(results);
    } finally {
      setSearching(false);
    }
  }}
/>

Example 4: Column-Specific Search

<DataTable
  columns={columns}
  data={data}
  searchConfig={{
    enabled: true,
    searchableColumns: ['name', 'sku', 'email'], // Only search these columns
    caseSensitive: false,
  }}
  onSearch={async (searchText, searchColumns) => {
    // searchColumns will be ['name', 'sku', 'email']
    await searchInColumns(searchText, searchColumns);
  }}
/>

Search Configuration Options:

| Property | Type | Default | Description | |----------|------|---------|-------------| | enabled | boolean | false | Enable/disable search bar | | placeholder | string | "Search..." | Placeholder text for search input | | searchButtonLabel | string | "Search" | Label text displayed on search button (shown with icon) | | clearButtonLabel | string | "Clear search" | Accessible label for clear button | | showClearButton | boolean | true | Show clear button when text exists | | searchOnType | boolean | false | Search while typing vs. button click only | | debounceMs | number | 300 | Debounce time for search-as-you-type (ms) | | caseSensitive | boolean | false | Case-sensitive search | | searchableColumns | string[] | [] | Specific columns to search (empty = all columns) | | minCharacters | number | 1 | Minimum characters before search triggers | | maxLength | number | 100 | Maximum length of search input | | showResultsCount | boolean | false | Show count of filtered results | | isSearching | boolean | false | Show loading state in search button | | ariaLabel | string | "Search table" | ARIA label for search input |

Search Behavior Modes:

  • Button Mode (searchOnType: false): Search only triggers when the search button is clicked or Enter key is pressed. Ideal for server-side searches with expensive operations.

  • Type Mode (searchOnType: true): Search triggers as the user types (with debouncing). Perfect for client-side filtering or real-time search experiences.

Keyboard Shortcuts:

  • Enter: Triggers search in both modes (bypasses debounce in type mode)
  • Escape: Clears search when showClearButton is enabled

Accessibility Features:

  • Full keyboard navigation support
  • Customizable ARIA labels for screen readers
  • Loading state announcements
  • High contrast mode support

Features:

  • Advanced Data Management: Sortable columns with intelligent data type handling and custom sort functions
  • Flexible Selection: Support for single-row selection or multi-row selection with visual feedback
  • Bulk Operations: Mass action system for performing operations on multiple selected items
  • Contextual Actions: Per-row actions with customizable button layouts, links, or dropdown menus
  • State Management: Built-in loading states, empty state handling, and error boundary support
  • Performance Optimized: Virtual scrolling for large datasets with efficient re-rendering
  • Scrollable Tables: Optional maxHeight prop for vertical scrolling with large datasets (v1.0.3+)
  • Search & Filter: Built-in search bar with button and type modes, column-specific filtering
  • User Experience: Intuitive interactions with hover states, keyboard navigation, and clear visual hierarchy

DataForm

A dynamic form builder that creates forms from configuration objects with built-in validation and multiple field types.

import React, { useState } from 'react';
import { DataForm, useRouteParams, FieldType } from '@adobe-commerce/aio-experience-kit';
import { ToastQueue } from '@react-spectrum/toast';

const EntityForm = ({ actionCallHeaders, id = undefined }) => {
  const entityModel = new EntityModel(actionCallHeaders);
  
  const [formFields, setFormFields] = useState({});
  const [editItem, setEditItem] = useState({});
  
  const { getNavigate } = useRouteParams();
  const navigate = getNavigate();

  return (
    <DataForm
      components={formFields}
      editItem={editItem}
      onFormLoad={async () => {
        // Load edit item first
        let editItem = {};
        if (id !== undefined) {
          editItem = await entityModel.load(id);
          setEditItem(editItem);
        }
        // Then load form fields
        const formFields = entityModel.fields(editItem);
        setFormFields(formFields);
      }}
      onFormSubmit={async (values) => {
        try {
          await entityModel.save(values);
          ToastQueue.positive('Entity saved successfully', { timeout: 5000 });
        } catch (e) {
          ToastQueue.negative(e.message, { timeout: 5000 });
        }
      }}
      onPostFormSubmit={() => {
        navigate('/entity/grid');
      }}
      onBackPress={() => {
        navigate('/entity/grid');
      }}
    />
  );
};

// Entity Model
class EntityModel {
  constructor(actionCallHeaders) {
    this.actionCallHeaders = actionCallHeaders;
  }

  async load(id) {
    // Load entity using API
    return {};
  }

  async save(formValues) {
    // Save entity using API
    return [];
  }

  fields(editItem) {
    return {
      groups: [
        {
          code: 'entity_settings',
          label: 'Entity Settings',
          fields: [
            {
              label: 'Name',
              code: 'name',
              db_field: 'name',
              type: FieldType.TEXT,
              required: true
            },
            {
              label: 'Status',
              code: 'status',
              db_field: 'status',
              type: FieldType.SELECT,
              required: true,
              options: [
                {
                  value: '1',
                  label: 'Enabled'
                },
                {
                  value: '0',
                  label: 'Disabled'
                }
              ]
            },
            {
              label: 'Categories',
              code: 'categories',
              db_field: 'categories',
              type: FieldType.MULTISELECT,
              required: false,
              options: [
                {
                  value: 'electronics',
                  label: 'Electronics'
                },
                {
                  value: 'clothing',
                  label: 'Clothing'
                },
                {
                  value: 'books',
                  label: 'Books'
                },
                {
                  value: 'home',
                  label: 'Home & Garden'
                }
              ]
            },
            {
              label: 'Featured Product',
              code: 'featured',
              db_field: 'featured',
              type: FieldType.TOGGLE,
              required: false
            },
            {
              label: 'Notifications',
              code: 'notifications',
              db_field: 'notifications',
              type: FieldType.TOGGLE,
              required: false,
              use_env_var: true
            }
          ]
        }
      ]
    };
  }
}
Field Types

The DataForm component supports various field types for different input scenarios:

Basic Input Fields:

  • FieldType.TEXT - Standard text input
  • FieldType.EMAIL - Email input with validation
  • FieldType.PASSWORD - Password input with masking
  • FieldType.NUMBER - Numeric input with validation
  • FieldType.URL - URL input with validation
  • FieldType.TEL - Telephone number input
  • FieldType.SEARCH - Search input with appropriate styling

Selection Fields:

  • FieldType.SELECT - Single selection dropdown with options
  • FieldType.MULTISELECT - Multiple selection with checkboxes in a scrollable container
  • FieldType.TOGGLE - Boolean toggle switch with '1'/'0' values

Display Fields:

  • FieldType.LABEL - Read-only display field for showing values

Field Configuration Examples:

// MULTISELECT field with scrollable options
{
  label: 'Product Categories',
  code: 'categories',
  db_field: 'categories',
  type: FieldType.MULTISELECT,
  required: false,
  options: [
    { value: 'electronics', label: 'Electronics' },
    { value: 'clothing', label: 'Clothing' },
    { value: 'books', label: 'Books' },
    { value: 'home', label: 'Home & Garden' },
    // ... more options (automatically scrollable when many)
  ]
}

// TOGGLE field with automatic default value
{
  label: 'Enable Feature',
  code: 'feature_enabled',
  db_field: 'feature_enabled',
  type: FieldType.TOGGLE,
  required: false
  // Automatically defaults to '0' if no value provided
  // Returns '1' when enabled, '0' when disabled
}

// Field with environment variable support
{
  label: 'API Endpoint',
  code: 'api_endpoint',
  db_field: 'api_endpoint',
  type: FieldType.TEXT,
  required: true,
  use_env_var: true  // Adds checkbox to use as environment variable
}
Dependent Field Visibility

The DataForm component supports conditional field visibility based on the values of other fields. This allows you to create dynamic forms that show or hide fields based on user selections.

Basic Example:

{
  groups: [
    {
      code: 'settings',
      label: 'Settings',
      fields: [
        {
          label: 'Enable Feature',
          code: 'feature_enabled',
          db_field: 'feature_enabled',
          type: FieldType.TOGGLE,
          required: false
        },
        {
          label: 'Feature Configuration',
          code: 'feature_config',
          db_field: 'feature_config',
          type: FieldType.TEXT,
          required: false,
          // This field only shows when feature_enabled is truthy
          dependsOn: {
            field: 'feature_enabled',
            condition: { type: 'truthy' }
          }
        }
      ]
    }
  ]
}

Dependency Condition Types:

// Equals - Show field when value matches exactly
dependsOn: {
  field: 'status',
  condition: { type: 'equals', value: 'active' }
}

// Not Equals - Show field when value doesn't match
dependsOn: {
  field: 'status',
  condition: { type: 'notEquals', value: 'disabled' }
}

// In - Show field when value is in array
dependsOn: {
  field: 'status',
  condition: { type: 'in', values: ['pending', 'processing'] }
}

// Not In - Show field when value is not in array
dependsOn: {
  field: 'status',
  condition: { type: 'notIn', values: ['cancelled', 'completed'] }
}

// Greater Than - Show field when numeric value is greater
dependsOn: {
  field: 'quantity',
  condition: { type: 'greaterThan', value: 10 }
}

// Less Than - Show field when numeric value is less
dependsOn: {
  field: 'stock_level',
  condition: { type: 'lessThan', value: 5 }
}

// Truthy - Show field when value is truthy (handles '0' and 'false' as falsy)
dependsOn: {
  field: 'enable_feature',
  condition: { type: 'truthy' }
}

// Falsy - Show field when value is falsy
dependsOn: {
  field: 'enable_feature',
  condition: { type: 'falsy' }
}

// Custom - Show field based on custom validation function
dependsOn: {
  field: 'username',
  condition: {
    type: 'custom',
    validator: (value, allFormValues) => {
      return value && value.length > 3;
    }
  }
}

Advanced Example - Multi-Step Configuration:

{
  groups: [
    {
      code: 'shipping',
      label: 'Shipping Configuration',
      fields: [
        {
          label: 'Shipping Method',
          code: 'shipping_method',
          db_field: 'shipping_method',
          type: FieldType.SELECT,
          required: true,
          options: [
            { value: 'standard', label: 'Standard Shipping' },
            { value: 'express', label: 'Express Shipping' },
            { value: 'pickup', label: 'Store Pickup' }
          ]
        },
        {
          label: 'Carrier',
          code: 'carrier',
          db_field: 'carrier',
          type: FieldType.SELECT,
          required: true,
          options: [
            { value: 'ups', label: 'UPS' },
            { value: 'fedex', label: 'FedEx' },
            { value: 'usps', label: 'USPS' }
          ],
          // Only show carrier selection for standard and express shipping
          dependsOn: {
            field: 'shipping_method',
            condition: { type: 'in', values: ['standard', 'express'] }
          }
        },
        {
          label: 'Tracking Number',
          code: 'tracking_number',
          db_field: 'tracking_number',
          type: FieldType.TEXT,
          required: false,
          // Only show tracking for shipped methods
          dependsOn: {
            field: 'shipping_method',
            condition: { type: 'notEquals', value: 'pickup' }
          }
        },
        {
          label: 'Store Location',
          code: 'store_location',
          db_field: 'store_location',
          type: FieldType.SELECT,
          required: true,
          options: [
            { value: 'store1', label: 'Main Street Store' },
            { value: 'store2', label: 'Downtown Store' }
          ],
          // Only show store location for pickup
          dependsOn: {
            field: 'shipping_method',
            condition: { type: 'equals', value: 'pickup' }
          }
        }
      ]
    }
  ]
}

Dependency Configuration:

interface FieldDependency {
  field: string;                    // The code of the field this depends on
  condition: DependencyCondition;   // The condition to evaluate
  clearValueOnHide?: boolean;       // Whether to clear field value when hidden (default: false)
}

Key Features:

  • Dynamic Visibility: Fields automatically show/hide based on dependency conditions
  • Multiple Conditions: Support for equals, notEquals, in, notIn, greaterThan, lessThan, truthy, falsy, and custom validators
  • Validation Integration: Hidden fields are excluded from form validation
  • Value Management: Optional automatic clearing of hidden field values
  • Performance Optimized: Uses React memoization to prevent unnecessary re-renders
  • Chain Dependencies: Fields can depend on other dependent fields
  • Form Submission: Hidden field values are automatically excluded from submission

Features:

  • Dynamic Form Generation: Create complex forms from simple configuration objects without manual field creation
  • Rich Field Types: Support for text, email, password, number, select, multiselect, toggle, and specialized input types
  • Conditional Field Visibility: Show/hide fields dynamically based on other field values with multiple condition types
  • Multi-Selection Support: MULTISELECT field type with React Spectrum CheckboxGroup for selecting multiple options with scrollable overflow and accessibility support
  • Toggle Controls: TOGGLE field type using React Spectrum Switch component with '1'/'0' value handling and automatic default value initialization
  • Environment Variable Support: Built-in support for environment variable checkboxes on compatible field types (TEXT, SELECT, MULTISELECT, TOGGLE)
  • Intelligent Validation: Built-in validation with custom rules, real-time feedback, and error messaging
  • Workflow Integration: Loading states, processing indicators, and navigation controls for multi-step workflows
  • Organized Layout: Logical field grouping with collapsible sections and intuitive information hierarchy
  • Developer Friendly: Event-driven architecture with lifecycle hooks for custom business logic integration
  • Configurable Buttons: Full control over form action buttons with custom labels, icons, visibility, positioning, and alignment
Button Configuration

The DataForm component provides extensive button customization through the buttonConfig prop:

Basic Usage:

<DataForm
  components={formFields}
  editItem={editItem}
  onFormLoad={onFormLoad}
  onFormSubmit={onFormSubmit}
  onBackPress={onBackPress}
  buttonConfig={{
    submitButton: { label: 'Create Product' },
    backButton: { label: 'Cancel' },
  }}
/>

Button Configuration Options:

interface FormButtonConfig {
  submitButton?: {
    label?: string;           // Button text (default: "Save")
    icon?: ReactElement;      // Custom icon (default: SaveFloppy)
    isHidden?: boolean;       // Hide button (default: false)
    ariaLabel?: string;       // Custom ARIA label
  };
  
  backButton?: {
    label?: string;           // Button text (default: "Back")
    icon?: ReactElement;      // Custom icon (default: Back)
    isHidden?: boolean;       // Hide button (default: false)
    ariaLabel?: string;       // Custom ARIA label
  };
  
  secondaryButtons?: Array<{
    key: string;              // Unique identifier
    label: string;            // Button text
    icon?: ReactElement;      // Optional icon
    onPress: () => Promise<void>; // Click handler
    variant?: 'primary' | 'secondary' | 'negative'; // Style variant
    isDisabled?: boolean;     // Disable state
    type?: 'button' | 'submit'; // Button type (default: 'button')
    ariaLabel?: string;       // Custom ARIA label
  }>;
  
  position?: 'before' | 'after';  // Button position relative to form (default: 'after')
  alignment?: 'start' | 'center' | 'end'; // Horizontal alignment (default: 'start')
  gap?: 'small' | 'medium' | 'large'; // Spacing between buttons (default: 'small')
}

Common Use Cases:

// Example 1: Create vs Edit Forms
<DataForm
  buttonConfig={{
    submitButton: { 
      label: isEditMode ? 'Update Product' : 'Create Product' 
    },
  }}
/>

// Example 2: Modal Forms (hide back button)
<DataForm
  buttonConfig={{
    submitButton: { label: 'Save Changes' },
    backButton: { isHidden: true },
  }}
/>

// Example 3: Wizard Forms with Navigation
<DataForm
  buttonConfig={{
    submitButton: { label: 'Next Step' },
    backButton: { label: 'Previous Step' },
  }}
/>

// Example 4: Draft Workflow with Secondary Action
<DataForm
  buttonConfig={{
    submitButton: { label: 'Save Draft' },
    secondaryButtons: [
      {
        key: 'publish',
        label: 'Publish',
        variant: 'primary',
        onPress: async () => {
          await handlePublish();
        },
      },
    ],
  }}
/>

// Example 5: Edit Form with Delete Action
<DataForm
  buttonConfig={{
    submitButton: { label: 'Update' },
    secondaryButtons: [
      {
        key: 'delete',
        label: 'Delete',
        variant: 'negative',
        onPress: async () => {
          const confirmed = await showConfirmDialog();
          if (confirmed) {
            await handleDelete();
            navigate('/products');
          }
        },
      },
    ],
  }}
/>

// Example 6: Top-Aligned Buttons for Long Forms
<DataForm
  buttonConfig={{
    submitButton: { label: 'Save' },
    position: 'before',
    alignment: 'end',
  }}
/>

// Example 7: Right-Aligned Buttons (Dialog Pattern)
<DataForm
  buttonConfig={{
    submitButton: { label: 'Confirm' },
    backButton: { label: 'Cancel' },
    alignment: 'end',
  }}
/>

// Example 8: Multi-Action Workflow
<DataForm
  buttonConfig={{
    submitButton: { label: 'Save' },
    secondaryButtons: [
      {
        key: 'save-continue',
        label: 'Save & Continue',
        onPress: async () => {
          await handleSave();
          navigateToNext();
        },
      },
      {
        key: 'save-new',
        label: 'Save & Add New',
        onPress: async () => {
          await handleSave();
          resetForm();
        },
      },
    ],
  }}
/>

// Example 9: Custom Icons and ARIA Labels
import SaveFloppy from '@spectrum-icons/workflow/SaveFloppy';
import CheckmarkCircle from '@spectrum-icons/workflow/CheckmarkCircle';

<DataForm
  buttonConfig={{
    submitButton: {
      label: 'Approve',
      icon: <CheckmarkCircle />,
      ariaLabel: 'Approve and save changes',
    },
  }}
/>

// Example 10: Centered Buttons with Custom Gap
<DataForm
  buttonConfig={{
    submitButton: { label: 'Submit' },
    backButton: { label: 'Cancel' },
    alignment: 'center',
    gap: 'large',
  }}
/>

Button Order and States:

  • Button Order: ProgressCircle → Submit → Back → Secondary buttons (in order)
  • Disabled States: All buttons are automatically disabled during form submission or when isProcessing is true
  • Loading States: ProgressCircle shows automatically during submission, secondary buttons can show individual loading states
  • Accessibility: All buttons have proper ARIA labels and keyboard navigation support

Migration Notes:

For existing implementations without buttonConfig, the default behavior is preserved:

  • Submit button labeled "Save" with SaveFloppy icon
  • Back button labeled "Back" with Back icon
  • Buttons positioned after the form with start (left) alignment
  • No breaking changes to existing code

ConfirmationDialog

A modal dialog component for user confirmations with customizable actions and styling. Perfect for delete confirmations and critical user decisions.

import React, { useState } from 'react';
import { View, ActionButton, Text } from '@adobe/react-spectrum';
import { DataTable, useRouteParams, ConfirmationDialog } from '@adobe-commerce/aio-experience-kit';

const EntityGrid = ({ actionCallHeaders }) => {
  const { getNavigate } = useRouteParams();
  const navigate = getNavigate();
  const [isProcessing, setProcessing] = useState(false);
  const [gridData, setGridData] = useState([]);
  const [deleteMessage, setDeleteMessage] = useState('');
  const [deleteKeys, setDeleteKeys] = useState([]);
  
  const entityModel = new EntityModel(actionCallHeaders);

  return (
    <View>
      <DataTable
        // ... other DataTable props
        onMassActionPress={async (key, ids) => {
          switch (key) {
            case 'delete':
              setDeleteKeys(ids);
              setDeleteMessage(`Are you sure you want to delete ${ids.length} selected items?`);
              break;
          }
        }}
        onGridActionPress={async (key, item) => {
          switch (key) {
            case 'delete':
              setDeleteKeys([item.id]);
              setDeleteMessage(`Are you sure you want to delete "${item.name}"?`);
              break;
          }
        }}
      />

      <ConfirmationDialog
        title="Delete Dialog"
        message={deleteMessage}
        primaryButtonVariant="negative"
        onDismiss={() => {
          setDeleteMessage('');
        }}
        onSecondaryPress={() => {
          setDeleteMessage('');
        }}
        onPrimaryPress={async () => {
          setDeleteMessage('');
          setProcessing(true);
          setGridData(await entityModel.delete(deleteKeys));
          setProcessing(false);
        }}
      />
    </View>
  );
};

// Entity Model
class EntityModel {
  constructor(actionCallHeaders) {
    this.actionCallHeaders = actionCallHeaders;
  }

  async delete(itemIds = []) {
    // Delete entity using API
    return [];
  }
}

Features:

  • Flexible Content: Customizable title, message, and button text to match any confirmation scenario
  • Action Variants: Support for different button styles (primary, secondary, negative) to indicate action severity
  • User Experience: Intuitive keyboard navigation with escape key dismissal and tab order management
  • Accessibility Excellence: Full screen reader support, proper ARIA labels, and focus trap implementation
  • Design Consistency: Follows Adobe Spectrum design patterns for familiar user interactions

FileUpload

A flexible file upload component with file validation and automatic content reading. Built on React Spectrum's FileTrigger for consistent Adobe design patterns.

import React, { useState } from 'react';
import { View } from '@adobe/react-spectrum';
import { FileUpload } from '@adobe-commerce/aio-experience-kit';
import { ToastQueue } from '@react-spectrum/toast';

const DocumentUploader = () => {
  const [isProcessing, setProcessing] = useState(false);

  return (
    <View>
      <FileUpload
        label="Upload Documents"
        isRequired={true}
        acceptedFileTypes={['application/pdf', '.doc', '.docx']}
        allowsMultiple={true}
        isDisabled={isProcessing}
        onSelect={async (files) => {
          setProcessing(true);
          try {
            // Process each file
          } catch (error) {
            ToastQueue.negative(`Upload failed: ${error.message}`, { timeout: 5000 });
          } finally {
            setProcessing(false);
          }
        }}
      />
    </View>
  );
};

Programmatic Control with Refs (v1.0.3+):

Reset the component programmatically from parent components:

import React, { useRef } from 'react';
import { FileUpload, FileUploadHandle } from '@adobe-commerce/aio-experience-kit';

const MyForm = () => {
  const fileUploadRef = useRef<FileUploadHandle>(null);
  
  const handleReset = () => {
    fileUploadRef.current?.reset(); // Clear all files
  };
  
  const handleGetFiles = () => {
    const files = fileUploadRef.current?.getFiles(); // Get current files
    console.log('Current files:', files);
  };
  
  return (
    <>
      <FileUpload
        ref={fileUploadRef}
        label="Upload Files"
        onSelect={(files) => console.log('Selected:', files)}
      />
      <Button onPress={handleReset}>Reset</Button>
      <Button onPress={handleGetFiles}>Get Files</Button>
    </>
  );
};

Features:

  • File Selection: Click to browse files with support for single or multiple file selection
  • Automatic Content Reading: Automatically reads file content as text or base64 based on file type
  • File Type Validation: Built-in validation for accepted MIME types and file extensions
  • Built-in Display: Automatically displays selected files with name, size, and type information
  • Clear Functionality: Built-in "Clear Files" link to remove selected files and programmatic reset via ref
  • Imperative Handle: Access reset() and getFiles() methods via React refs for parent control
  • Loading States: Shows processing indicator while reading file contents
  • Error Handling: Comprehensive error messaging for file reading failures
  • Accessibility: Full keyboard navigation, screen reader support, and ARIA labels
  • Type Safety: Complete TypeScript support with FileInfo interface for processed files

🔧 Utils

Utility functions and classes that simplify common tasks in Adobe Commerce extension development.

AdminUiSdk

A utility class for managing Adobe IMS credentials in Adobe Admin UI extensions. Provides secure access to authentication tokens and organization IDs for API calls.

Note: As of v1.0.2, registration methods have been removed. Use the Adobe UIX SDK directly for extension registration.

Getting Adobe IMS Credentials

Retrieve authentication credentials from the Adobe shared context:

import { AdminUiSdk } from '@adobe-commerce/aio-experience-kit';

try {
  const sdk = new AdminUiSdk('my_admin_extension');

  const credentials = await sdk.getCredentials();
  
  if (credentials.imsToken && credentials.imsOrgId) {
    // Use credentials for API calls
    const apiHeaders = {
      'Authorization': `Bearer ${credentials.imsToken}`,
      'x-gw-ims-org-id': credentials.imsOrgId
    };
    
    // Make authenticated API requests
  } else {
    console.log('Credentials not available');
  }
} catch (error) {
  console.error('Failed to get credentials:', error.message);
}

Features:

  • Credential Management: Secure access to Adobe IMS authentication tokens and organization IDs
  • Shared Context Access: Retrieves credentials from Adobe's shared context using @adobe/uix-guest
  • Error Handling: Comprehensive error handling for credential retrieval failures
  • Type Safety: Full TypeScript support with intelligent code completion and compile-time validation
  • Production Ready: Battle-tested utility used in real Adobe Commerce extensions

useRouteParams

A React hook that provides a clean API for accessing React Router parameters and navigation functions.

import { useRouteParams } from '@adobe-commerce/aio-experience-kit';

const ProductDetail = () => {
  const { getParam, getNavigate } = useRouteParams();
  
  const productId = getParam('id');
  const navigate = getNavigate();
  
  const handleBack = () => {
    navigate('/products');
  };
  
  return (
    <div>
      <h1>Product {productId}</h1>
      <button onClick={handleBack}>Back to Products</button>
    </div>
  );
};

Features:

  • Parameter Access: Type-safe access to URL parameters with automatic type inference and null safety
  • Navigation Control: Wrapped navigation function with programmatic routing and history management
  • Clean API: Simplified interface that abstracts React Router complexity while maintaining full functionality
  • Hook Integration: Seamless integration with React component lifecycle and state management patterns
  • Error Handling: Built-in protection against navigation errors and invalid route parameters
  • Performance Optimized: Efficient parameter extraction and navigation with minimal re-renders

TypeScript Support

This library is built with TypeScript and provides comprehensive type definitions for all components and utilities. Import types as needed:

import type {
  MainContainerProps,
  DataTableProps,
  DataFormProps,
  ConfirmationDialogProps,
  FileUploadProps,
  FileUploadHandle,
  FieldType,
  AdminUiSdkInterface,
  AdminUiSdkCredentials
} from '@adobe-commerce/aio-experience-kit';

// Deprecated types (for backward compatibility only)
// These types are deprecated and will be removed in v2.0.0
// import type { 
//   ShippingCarrierFormProps, 
//   SelectOption,
//   AdminUiSdkRegistration,
//   MenuItem
// } from '@adobe-commerce/aio-experience-kit';

License

See the LICENSE file (in package) for license rights and limitations.