BigBlocks Docs
Components/Backup & Recovery

BackupImport

Component for importing Bitcoin wallet backups from various formats

backup-recoveryonboardingsecurity

BackupImport combines native HTML file input with Bitcoin backup processing, providing a streamlined interface for importing encrypted wallet backups with validation, profile synchronization, and secure file handling.

Basic Import

Already have a backup? Import it instead
Import backup fileSupports encrypted (.txt) and unencrypted (.json) backups
.json,.txt,.backup

With Custom Styling

Already have a backup? Import it instead
Import backup fileSupports encrypted (.txt) and unencrypted (.json) backups
.json,.txt,.backup

Demo Note: This demonstrates the backup import interface. In a real application, uploaded files would be validated and processed securely.

Installation

npx bigblocks add backup-import

Import

import { BackupImport } from 'bigblocks';

This component extends native HTML file input functionality with Bitcoin-specific backup processing.

Props

PropTypeRequiredDefaultDescription
onImport(file: File) => Promise<void> | voidYes-Handler for file import
disabledbooleanNofalseDisable the import button
classNamestringNo-Additional CSS classes
showLabelbooleanNotrueShow the label text
enableProfileSyncbooleanNofalseEnable profile synchronization
maxDiscoveryAttemptsnumberNo3Max attempts for profile discovery
onImportSuccess() => voidNo-Callback when import succeeds

Basic Usage

import { BackupImport } from 'bigblocks';

export default function RestorePage() {
  const handleImport = async (file: File) => {
    try {
      // Process the backup file
      const content = await file.text();
      
      // Your backup processing logic
      await processBackup(content);
      
      console.log('Backup imported successfully');
    } catch (error) {
      console.error('Import failed:', error);
      throw error; // Re-throw to show error in UI
    }
  };

  return (
    <BackupImport
      onImport={handleImport}
      onImportSuccess={() => {
        console.log('Import completed!');
      }}
    />
  );
}

Advanced Usage

With Authentication Manager

import { BackupImport } from 'bigblocks';
import { useAuthManager } from '@/hooks/useAuthManager';

export default function AuthBackupImport() {
  const authManager = useAuthManager();

  return (
    <BackupImport
      onImport={async (file) => {
        await authManager.importBackup(file);
      }}
      onImportSuccess={() => {
        router.push('/dashboard');
      }}
    />
  );
}

With Profile Sync

import { BackupImport } from 'bigblocks';

export default function ProfileSyncImport() {
  return (
    <BackupImport
      onImport={handleBackupImport}
      enableProfileSync={true}
      maxDiscoveryAttempts={5}
      onImportSuccess={() => {
        toast.success('Backup imported with profile sync');
      }}
    />
  );
}

Disabled State

import { BackupImport } from 'bigblocks';
import { useState } from 'react';

export default function ConditionalImport() {
  const [isProcessing, setIsProcessing] = useState(false);

  return (
    <BackupImport
      onImport={async (file) => {
        setIsProcessing(true);
        try {
          await processBackupFile(file);
        } finally {
          setIsProcessing(false);
        }
      }}
      disabled={isProcessing}
      showLabel={!isProcessing}
    />
  );
}

Custom Styling

import { BackupImport } from 'bigblocks';

export default function StyledImport() {
  return (
    <BackupImport
      onImport={handleImport}
      className="border-2 border-dashed border-orange-300 p-6 rounded-lg"
      showLabel={true}
    />
  );
}

Common Patterns

In Restore Flow

import { BackupImport } from 'bigblocks';
import { useState } from 'react';

export function RestoreFlow() {
  const [step, setStep] = useState<'import' | 'password' | 'complete'>('import');
  const [backupFile, setBackupFile] = useState<File | null>(null);

  return (
    <div className="max-w-md mx-auto">
      {step === 'import' && (
        <div>
          <h2 className="text-xl font-bold mb-4">Import Your Backup</h2>
          <BackupImport
            onImport={async (file) => {
              setBackupFile(file);
              setStep('password');
            }}
          />
        </div>
      )}
      
      {step === 'password' && backupFile && (
        <div>
          <h2 className="text-xl font-bold mb-4">Enter Password</h2>
          <PasswordInput
            onSubmit={async (password) => {
              await decryptAndImport(backupFile, password);
              setStep('complete');
            }}
          />
        </div>
      )}
      
      {step === 'complete' && (
        <div>
          <h2 className="text-xl font-bold mb-4">Import Complete!</h2>
          <button onClick={() => router.push('/dashboard')}>
            Go to Dashboard
          </button>
        </div>
      )}
    </div>
  );
}

With Error Handling

import { BackupImport } from 'bigblocks';
import { useState } from 'react';

export function ImportWithErrors() {
  const [error, setError] = useState<string | null>(null);

  return (
    <div>
      {error && (
        <div className="bg-red-100 text-red-700 p-3 rounded mb-4">
          {error}
        </div>
      )}
      
      <BackupImport
        onImport={async (file) => {
          setError(null);
          
          try {
            // Validate file
            if (!file.name.match(/\.(txt|json|enc)$/)) {
              throw new Error('Invalid file type. Please select a backup file.');
            }
            
            if (file.size > 10 * 1024 * 1024) {
              throw new Error('File too large. Maximum size is 10MB.');
            }
            
            await processBackup(file);
          } catch (err) {
            setError(err.message);
            throw err; // Re-throw for component error handling
          }
        }}
        onImportSuccess={() => {
          setError(null);
          toast.success('Backup imported successfully');
        }}
      />
    </div>
  );
}

Multiple Import Options

import { BackupImport } from 'bigblocks';

export function ImportOptions() {
  return (
    <div className="space-y-6">
      <div>
        <h3 className="font-semibold mb-2">Import from File</h3>
        <BackupImport
          onImport={handleFileImport}
          onImportSuccess={() => {
            console.log('File import complete');
          }}
        />
      </div>
      
      <div className="divider">OR</div>
      
      <div>
        <h3 className="font-semibold mb-2">Import from Cloud</h3>
        <button onClick={handleCloudImport}>
          Import from Google Drive
        </button>
      </div>
    </div>
  );
}

File Handling

The component accepts various backup file formats:

async function handleImport(file: File) {
  const content = await file.text();
  
  // Detect format
  if (content.startsWith('U2FsdGVkX1')) {
    // Encrypted backup
    const password = await promptForPassword();
    const decrypted = decrypt(content, password);
    await restoreFromBackup(decrypted);
  } else if (content.startsWith('{')) {
    // JSON backup
    const backup = JSON.parse(content);
    await restoreFromBackup(backup);
  } else if (content.match(/^[KL5][1-9A-HJ-NP-Za-km-z]{50,51}$/)) {
    // WIF format
    await restoreFromWIF(content);
  } else {
    throw new Error('Unknown backup format');
  }
}

Styling

The component uses Radix Themes and can be customized:

/* Default structure */
.backup-import-container {
  /* Button styling from Radix */
}

.backup-import-input {
  /* Hidden file input */
  display: none;
}

Best Practices

  1. Validation: Always validate file format and size before processing
  2. Error Handling: Provide clear error messages for common issues
  3. Progress Feedback: Show loading state during import
  4. Success Confirmation: Always confirm successful import
  5. Security: Never log or expose sensitive backup data

Security Considerations

  1. Client-Side Processing: Handle backup data only in the browser
  2. File Validation: Check file size and format before processing
  3. Password Protection: Prompt for passwords for encrypted backups
  4. Memory Cleanup: Clear sensitive data from memory after use
  5. Error Messages: Don't expose sensitive information in errors

Troubleshooting

Import Button Not Working

  • Check that onImport handler is properly defined
  • Ensure component isn't disabled
  • Verify file input isn't blocked by browser

File Not Processing

  • Check file format is supported
  • Verify file isn't corrupted
  • Ensure async operations are properly handled

Success Callback Not Firing

  • Make sure onImport doesn't throw errors
  • Verify onImportSuccess prop is passed
  • Check that import process completes successfully

API Reference

File Processing

The onImport handler receives a File object:

interface File {
  name: string;
  size: number;
  type: string;
  lastModified: number;
  text(): Promise<string>;
  arrayBuffer(): Promise<ArrayBuffer>;
}

Error Handling

Errors thrown in onImport will be caught and displayed by the component. Return or throw errors to show them to users.

Notes for Improvement

Major Simplification: The actual component is much simpler than the prompt described. It lacks:

  • Multiple format support options
  • Built-in validation
  • Progress tracking
  • Format detection
  • The extensive backend API integration described
  • Drag and drop functionality
  • Conflict resolution
  • Preview capabilities

The actual implementation is a basic file input button that delegates all processing to the onImport handler.