IdentityGeneration
Component for generating new Bitcoin identities or importing existing ones
A streamlined component that provides options to generate a new Bitcoin identity or import an existing backup. It offers a simple interface with two primary actions, perfect for onboarding flows.
Create Your Identity
Generate a new Bitcoin identity or import existing backupWhat is a BAP Identity?A BAP (Bitcoin Attestation Protocol) identity uses cryptographic keys to authenticate you across applications. While you'll create a password to encrypt and protect your keys locally, you won't need passwords to log into websites - your BAP ID proves who you are cryptographically.
Security Note: Generated identities use secure random number generation. Private keys are never transmitted and remain in your browser.
Installation
npx bigblocks add identity-generationImport
import { IdentityGeneration } from 'bigblocks';Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| onGenerate | () => void | Yes | - | Handler when generate button is clicked |
| onImport | (file: File) => void | Yes | - | Handler when import file is selected |
| loading | boolean | No | false | Show loading state |
| error | string | No | - | Error message to display |
| className | string | No | - | Additional CSS classes |
Basic Usage
import { IdentityGeneration } from 'bigblocks';
import { useRouter } from 'next/navigation';
export default function OnboardingPage() {
const router = useRouter();
const handleGenerate = () => {
// Navigate to identity creation flow
router.push('/create-identity');
};
const handleImport = (file: File) => {
// Process the imported file
console.log('Importing backup file:', file.name);
// Navigate to import flow
router.push('/import-backup');
};
return (
<IdentityGeneration
onGenerate={handleGenerate}
onImport={handleImport}
/>
);
}Advanced Usage
With Loading State
import { IdentityGeneration } from 'bigblocks';
import { useState } from 'react';
export default function LoadingExample() {
const [loading, setLoading] = useState(false);
const handleGenerate = async () => {
setLoading(true);
try {
// Simulate async operation
await new Promise(resolve => setTimeout(resolve, 2000));
// Navigate or process
} finally {
setLoading(false);
}
};
return (
<IdentityGeneration
onGenerate={handleGenerate}
onImport={(file) => console.log(file)}
loading={loading}
/>
);
}With Error Handling
import { IdentityGeneration } from 'bigblocks';
import { useState } from 'react';
export default function ErrorHandlingExample() {
const [error, setError] = useState<string | undefined>();
const handleImport = (file: File) => {
setError(undefined);
// Validate file
if (!file.name.match(/\.(txt|json|enc)$/)) {
setError('Invalid file type. Please select a backup file.');
return;
}
if (file.size > 10 * 1024 * 1024) {
setError('File too large. Maximum size is 10MB.');
return;
}
// Process file
processBackupFile(file);
};
return (
<IdentityGeneration
onGenerate={() => console.log('Generate clicked')}
onImport={handleImport}
error={error}
/>
);
}Custom Styling
import { IdentityGeneration } from 'bigblocks';
export default function StyledExample() {
return (
<div className="bg-gray-50 p-8 rounded-lg">
<h2 className="text-2xl font-bold mb-6">Get Started</h2>
<IdentityGeneration
onGenerate={() => console.log('Generate')}
onImport={(file) => console.log('Import', file)}
className="max-w-md mx-auto"
/>
</div>
);
}Common Patterns
In Onboarding Flow
import { IdentityGeneration } from 'bigblocks';
import { useState } from 'react';
export function OnboardingFlow() {
const [step, setStep] = useState<'choice' | 'generate' | 'import'>('choice');
return (
<div className="min-h-screen flex items-center justify-center">
{step === 'choice' && (
<div>
<h1 className="text-3xl font-bold mb-8 text-center">
Welcome to BigBlocks
</h1>
<IdentityGeneration
onGenerate={() => setStep('generate')}
onImport={(file) => {
// Store file for processing
sessionStorage.setItem('importFile', file.name);
setStep('import');
}}
/>
</div>
)}
{step === 'generate' && (
<div>
<h2>Generate New Identity</h2>
{/* Identity creation component */}
</div>
)}
{step === 'import' && (
<div>
<h2>Import Backup</h2>
{/* Backup import component */}
</div>
)}
</div>
);
}With File Processing
import { IdentityGeneration } from 'bigblocks';
export function ProcessingExample() {
const processFile = async (file: File) => {
const content = await file.text();
// Detect format
if (content.startsWith('U2FsdGVkX1')) {
console.log('Processing encrypted backup');
// Handle encrypted backup
} else if (content.startsWith('{')) {
console.log('Processing JSON backup');
// Handle JSON backup
} else {
console.log('Processing WIF backup');
// Handle WIF format
}
};
return (
<IdentityGeneration
onGenerate={() => {
console.log('Creating new identity');
}}
onImport={processFile}
/>
);
}With Navigation
import { IdentityGeneration } from 'bigblocks';
import { useRouter } from 'next/navigation';
export function NavigationExample() {
const router = useRouter();
return (
<IdentityGeneration
onGenerate={() => {
router.push('/auth/create-identity');
}}
onImport={(file) => {
// Store file reference
const fileUrl = URL.createObjectURL(file);
sessionStorage.setItem('importFileUrl', fileUrl);
router.push('/auth/import-backup');
}}
/>
);
}In Modal Dialog
import { IdentityGeneration } from 'bigblocks';
import { useState } from 'react';
export function ModalExample() {
const [showModal, setShowModal] = useState(true);
return (
<>
{showModal && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center p-4">
<div className="bg-white rounded-lg p-6 max-w-md w-full">
<h2 className="text-xl font-bold mb-4">Get Started</h2>
<IdentityGeneration
onGenerate={() => {
setShowModal(false);
// Start generation flow
}}
onImport={(file) => {
setShowModal(false);
// Start import flow
}}
/>
<button
onClick={() => setShowModal(false)}
className="mt-4 text-sm text-gray-500"
>
Cancel
</button>
</div>
</div>
)}
</>
);
}File Handling
The component uses a hidden file input that accepts common backup formats:
// Internal file input configuration
<input
type="file"
accept=".txt,.json,.enc"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) onImport(file);
}}
/>Styling
The component uses Radix Themes Button components and can be customized with the className prop:
/* Default button arrangement */
.identity-generation {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Custom styling example */
.custom-identity-generation {
max-width: 400px;
margin: 0 auto;
}Best Practices
- Clear Actions: The two buttons clearly indicate the available options
- Error Feedback: Always provide error messages for invalid files
- Loading States: Show loading state during async operations
- File Validation: Validate file type and size before processing
- Navigation: Handle navigation after user selection
Accessibility
- Both buttons are keyboard accessible
- Clear visual feedback for hover and focus states
- Error messages are announced to screen readers
- File input is properly labeled
Troubleshooting
Import Not Working
- Check that onImport handler is defined
- Verify file types are accepted
- Ensure file input isn't blocked by browser
Generate Not Triggering
- Verify onGenerate handler is provided
- Check for JavaScript errors in console
- Ensure component isn't disabled via loading state
Styling Issues
- Check className is applied correctly
- Verify Radix Themes is properly configured
- Inspect for CSS conflicts
Related Components
- BackupImport - Detailed backup import
- MnemonicDisplay - Display recovery phrases
- BackupDownload - Download backups
- SignupFlow - Complete signup process
API Reference
This component provides a simple interface for choosing between generating a new Bitcoin identity or importing an existing backup.
Handler Types
type GenerateHandler = () => void;
type ImportHandler = (file: File) => void;File Object
The File object passed to onImport contains:
interface File {
name: string;
size: number;
type: string;
lastModified: number;
text(): Promise<string>;
arrayBuffer(): Promise<ArrayBuffer>;
}Notes for Improvement
Simplified Implementation: The actual component is much simpler than the prompt described. It lacks:
- Built-in identity generation logic (just triggers a callback)
- Multiple generation modes (mnemonic vs random)
- Direct identity/backup object handling
- showImport prop to hide import option
- generateMode prop for different generation methods
The actual implementation is a basic choice component with two buttons that delegate all logic to the parent component via callbacks.