Skip to content

[Feature Request] Custom Loading & Error Fallback UI Props (renderLoading / renderError) #82

Description

@SB2318

Feature Request: Custom Loading and Error States for ImageEditor

Labels: enhancement, ui, dx

Description

Currently, while loadScript fetches the embed script from the CDN and initializes the editor, the container rendered by <ImageEditor /> remains an empty <div>.

If the network connection is slow, the CDN request takes longer than expected, or the script fails to load, users may see a blank container with no indication of what is happening.

This creates a poor user experience, particularly in applications where the editor is an important part of the workflow.

Proposed Solution

Introduce customizable loading and error UI for <ImageEditor />.

Host applications should be able to provide their own React nodes for both states, allowing them to render:

  • Loading spinners
  • Skeleton loaders
  • Static images
  • Animated GIFs
  • Branded illustrations
  • Error messages
  • Retry buttons
  • Custom recovery UIs

For example:

<ImageEditor
  loadingFallback={<MyLoadingComponent />}
  errorFallback={<MyErrorComponent />}
/>

Alternatively, render props could be supported:

<ImageEditor
  renderLoading={() => <MyLoadingComponent />}
  renderError={(error) => <MyErrorComponent error={error} />}
/>

Default Loading and Error UI

In addition to customization, the library could provide sensible default states so users do not see a blank container when they do not provide custom fallbacks.

For example:

Default Loading State

Display a simple loading indicator or default loading image while the embed script is being fetched and the editor is initializing.

The default could potentially support an image/GIF asset:

<ImageEditor
  loadingImage="/images/editor-loading.gif"
/>

Default Error State

If the script fails to load, display a default error illustration/message instead of leaving the container blank.

For example:

Unable to load the image editor.
Please check your connection and try again.

A retry action could also be provided:

<ImageEditor
  errorFallback={(error, retry) => (
    <div>
      <img src="/images/editor-error.gif" alt="Editor failed to load" />
      <p>Unable to load the editor.</p>
      <button onClick={retry}>Retry</button>
    </div>
  )}
/>

API Proposal

One possible API is:

interface ImageEditorProps {
  // existing props...

  loadingFallback?: React.ReactNode;

  errorFallback?:
    | React.ReactNode
    | ((error: Error, retry: () => void) => React.ReactNode);
}

Another option is to use render props consistently:

interface ImageEditorProps {
  // existing props...

  renderLoading?: () => React.ReactNode;

  renderError?: (
    error: Error,
    retry: () => void
  ) => React.ReactNode;
}

The render-prop approach provides more flexibility because the error state can expose the actual error and a retry mechanism.

Suggested Lifecycle

The component could maintain a simple initialization state:

idle
  ↓
loading
  ↓
ready
  ↓
error

Loading

When loadScript starts:

loading

Render the configured loading UI.

Ready

Once the script has successfully loaded and the editor has initialized:

ready

Render the actual editor.

Error

If the script fails to load or initialization fails:

error

Render the configured error UI.

If a retry function is provided, retrying should transition the component back to:

error → loading → ready

or back to error if the retry fails again.

Custom Images and GIFs

A useful extension would be allowing applications to provide an image or GIF directly without requiring them to build a React component:

<ImageEditor
  loadingImage="/assets/editor-loading.gif"
  errorImage="/assets/editor-error.png"
/>

This would make it easy for applications to use their own branding while keeping the API simple.

However, loadingFallback / errorFallback should remain available for applications that need complete control over the UI.

Example

<ImageEditor
  loadingFallback={
    <div className="editor-loading">
      <img
        src="/assets/editor-loading.gif"
        alt="Loading image editor"
      />
      <p>Loading editor...</p>
    </div>
  }
  errorFallback={(error, retry) => (
    <div className="editor-error">
      <img
        src="/assets/editor-error.png"
        alt="Unable to load image editor"
      />
      <p>Unable to load the image editor.</p>

      <button onClick={retry}>
        Try Again
      </button>
    </div>
  )}
/>

Acceptance Criteria

  • <ImageEditor /> exposes a loading-state customization API.
  • <ImageEditor /> exposes an error-state customization API.
  • The loading UI is displayed while the embed script is being loaded/initialized.
  • The error UI is displayed when script loading or initialization fails.
  • Applications can provide arbitrary React nodes for both states.
  • The error state can optionally expose the underlying error.
  • A retry mechanism can be provided to applications.
  • A sensible default loading UI is displayed when no custom fallback is provided.
  • A sensible default error UI is displayed when no custom fallback is provided.
  • Applications can optionally customize the default loading/error visuals with images or GIFs.
  • Existing <ImageEditor /> usage remains backward compatible.
  • Tests cover loading, successful initialization, error, and retry states.

Expected Outcome

Instead of:

┌─────────────────────────────┐
│                             │
│                             │
│        BLANK CONTAINER      │
│                             │
│                             │
└─────────────────────────────┘

users should see meaningful feedback:

┌─────────────────────────────┐
│                             │
│       [Loading GIF]         │
│      Loading editor...      │
│                             │
└─────────────────────────────┘

and, when loading fails:

┌─────────────────────────────┐
│                             │
│       [Error Image]         │
│  Unable to load the editor  │
│                             │
│        [ Try Again ]        │
│                             │
└─────────────────────────────┘

This improves both user experience and developer experience while giving applications the flexibility to match their own branding and UX requirements.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions