Customizable Button Progress Indicators with XState and shadcn/ui – ProgressButton

Add visual progress to buttons in your React app with ProgressButton. Uses XState & integrates with shadcn/ui.

ProgressButton is a UI component for shadcn/ui that visually indicates progress during asynchronous operations like file uploads or form submissions.

It extends the button component from shadcn/ui and integrates XState for state management.

Features

🔄 Manual & Automatic Progress: Control progress updates manually or use the automatic simulation for timed operations.

🎨 Customizable Appearance: Modify the success state color using Tailwind CSS classes.

⚙️ State Management: Leverages XState for predictable and robust state transitions (idle, in-progress, success, error).

👁️ Visual Feedback: Clearly indicates the current state: idle, processing, success, or error.

Use Cases

  • File Uploads: Show the percentage of a file upload directly on the upload button.
  • Form Submissions: Indicate that a form is processing after submission and show success upon completion.
  • Background Tasks: Display progress for longer-running background operations initiated by a button click.
  • Data Fetching: Provide feedback when a button triggers a data fetch operation.
  • Step-by-Step Processes: Guide users through multi-step actions where each step’s completion can be shown.

Usage

Integrate ProgressButton into your React components like any standard button. Pass props to configure its behavior and appearance.

Here is a basic implementation demonstrating automatic progress:

import React from "react";
import ProgressButton from "./ProgressButton"; // Adjust the import path
function App() {
  const handleAction = () => {
    console.log("Operation started");
    // Your async logic here
  };
  const handleCompletion = () => {
    console.log("Operation completed successfully");
  };
  const handleError = (err) => {
    console.error("Operation failed:", err);
    // Handle error state if needed
  };
  return (
    <div>
      <ProgressButton
        progressType="automatic" // Default is 'automatic'
        totalDuration={3000} // 3 seconds
        numberOfProgressSteps={6}
        successColorClass="emerald-500" // Example Tailwind color
        onClick={handleAction}
        onComplete={handleCompletion}
        onError={handleError}
      >
        Submit Data
      </ProgressButton>
    </div>
  );
}
export default App;

For manual control, set progressType to "manual" and update the progress prop (0-100) based on your application’s state.

import React, { useState, useEffect } from "react";
import ProgressButton from "./ProgressButton"; // Adjust the import path
function ManualProgressExample() {
  const [uploadProgress, setUploadProgress] = useState(0);
  const [isUploading, setIsUploading] = useState(false);
  // Simulate upload progress
  useEffect(() => {
    let interval;
    if (isUploading && uploadProgress < 100) {
      interval = setInterval(() => {
        setUploadProgress(prev => Math.min(prev + 10, 100));
      }, 500);
    } else if (uploadProgress >= 100) {
      setIsUploading(false);
      // onComplete callback would trigger here via the component's state machine
    }
    return () => clearInterval(interval);
  }, [isUploading, uploadProgress]);
  const handleStartUpload = () => {
    setUploadProgress(0);
    setIsUploading(true);
    console.log("Upload started");
    // Trigger actual upload logic here
  };
   const handleCompletion = () => {
    console.log("Upload completed successfully");
    setIsUploading(false); // Reset state if needed
  };

  return (
    <div>
      <ProgressButton
        progressType="manual"
        progress={uploadProgress}
        onClick={handleStartUpload}
        onComplete={handleCompletion}
        successColorClass="blue-500"
      >
        {isUploading ? `Uploading... ${uploadProgress}%` : "Upload File"}
      </ProgressButton>
    </div>
  );
}
export default ManualProgressExample;

Props

The ProgressButton component accepts the following props to control its behavior and appearance:

Base Props

PropTypeDefaultDescription
successColorClassstringundefinedTailwind color class for the success state (e.g., green-500). Do not include prefixes like bg-. Requires Tailwind setup.
onClickfunctionundefinedFunction called when the button is clicked in its idle state.
onCompletefunctionundefinedFunction called when the progress successfully completes and transitions out of the success state.
onErrorfunctionundefinedFunction called if an error occurs (Note: Error state handling might need custom implementation based on the provided info).

Progress Type and Manual Control

PropTypeDefaultDescription
progressTypestringmanualSet to "manual" to control progress via the progress prop.
progressnumber0Current progress value (0-100). Used only when progressType is "manual".

Automatic Progress Control

PropTypeDefaultDescription
progressTypestringautomaticSet to "automatic" for simulated progress. This is the default if not specified.
totalDurationnumber5000Total time in milliseconds for the automatic progress simulation.
numberOfProgressStepsnumber5Number of discrete steps the automatic progress simulation is divided into.

Related Resources

FAQs

Q: How does the automatic progress simulation work?
A: Automatic progress divides the totalDuration into numberOfProgressSteps. The time between steps is slightly randomized to create a more natural feel, but the total time remains consistent.

Q: Can I use custom hex colors for the success state?
A: Currently, successColorClass expects a standard Tailwind color name (e.g., red-500). Using arbitrary hex values like bg-[#ff0000] directly is not supported due to Tailwind’s Just-in-Time compilation requirements. You must use predefined Tailwind classes or safelist them in your configuration.

Q: What happens when manual progress reaches 100%?
A: When the progress prop in manual mode reaches or exceeds 100, the component’s internal state machine transitions to the success state, shows the success indication, and then returns to the idle state after a brief delay.

Q: What’s the difference between automatic and manual progress modes?
A: In automatic mode, the button simulates progress based on the configured duration and steps. In manual mode, you control the progress value directly through the progress prop.

Q: Can I integrate ProgressButton with form libraries?
A: Yes, ProgressButton works well with form libraries. Use the onClick event to trigger form submission and update progress manually or use automatic mode during submission.

Preview

tomredman

tomredman

Leave a Reply

Your email address will not be published. Required fields are marked *