Migration Guide
This guide will help you migrate your application to use the latest version of Ethereum Identity Kit with the new consolidated API structure.
Breaking Changes Overview
The latest version introduces several breaking changes focused on API consolidation, enhanced error handling, and improved accessibility:
API Consolidation
- ProfileCard & FullWidthProfile:
options
→extraOptions
- Prefetched data structure: Flat props → Nested
prefetched
object - FollowersYouKnow:
displayEmpty
→showEmpty
- FollowButton:
followButton
→customFollowButton
- FullWidthProfile:
role
prop moved toextraOptions
- ProfileStats:
prefetchedStats
/isPrefetchedStatsLoading
→prefetched
object
Enhanced Features
- useFollowButton: Extended return type with error handling and accessibility
- FollowButton: New error display and accessibility features
- Address Validation: Automatic validation using viem’s
isAddress
Step-by-Step Migration
1. Update ProfileCard Components
Before (Old API)
<ProfileCard
addressOrName="vitalik.eth"
options={{
followButton: <CustomFollowButton />,
nameMenu: <CustomMenu />,
profileData: profileData,
prefetchedProfileLoading: isProfileLoading,
refetchProfileData: refetchProfile,
statsData: statsData,
prefetchedStatsLoading: isStatsLoading,
refetchStatsData: refetchStats,
}}
/>
After (New API)
<ProfileCard
addressOrName="vitalik.eth"
extraOptions={{
customFollowButton: <CustomFollowButton />,
nameMenu: <CustomMenu />,
}}
prefetched={{
profile: {
data: profileData,
isLoading: isProfileLoading,
refetch: refetchProfile,
},
stats: {
data: statsData,
isLoading: isStatsLoading,
refetch: refetchStats,
},
}}
/>
2. Update FullWidthProfile Components
Before (Old API)
<FullWidthProfile
addressOrName="vitalik.eth"
role="Developer"
options={{
followButton: <CustomFollowButton />,
nameMenu: <CustomMenu />,
profileData: profileData,
prefetchedProfileLoading: isProfileLoading,
refetchProfileData: refetchProfile,
openListSettings: () => setShowSettings(true),
}}
/>
After (New API)
<FullWidthProfile
addressOrName="vitalik.eth"
extraOptions={{
role: "Developer",
customFollowButton: <CustomFollowButton />,
nameMenu: <CustomMenu />,
openListSettings: () => setShowSettings(true),
}}
prefetched={{
profile: {
data: profileData,
isLoading: isProfileLoading,
refetch: refetchProfile,
},
}}
/>
3. Update FollowersYouKnow Components
Before (Old API)
<FollowersYouKnow
lookupAddress="vitalik.eth"
connectedAddress="0x123..."
displayEmpty={false}
/>
After (New API)
<FollowersYouKnow
lookupAddress="vitalik.eth"
connectedAddress="0x123..."
showEmpty={false}
showLoading={true}
/>
4. Update FollowButton Usage
If you were using the FollowButton through component options:
Before (Old API)
const customFollowButton = (
<FollowButton
lookupAddress={lookupAddress}
connectedAddress={connectedAddress}
/>
)
<ProfileCard
addressOrName="vitalik.eth"
options={{ followButton: customFollowButton }}
/>
After (New API)
const customFollowButton = (
<FollowButton
lookupAddress={lookupAddress}
connectedAddress={connectedAddress}
customOnClick={(buttonState) => {
console.log('Button state:', buttonState)
return false // Continue with default behavior
}}
/>
)
<ProfileCard
addressOrName="vitalik.eth"
extraOptions={{ customFollowButton }}
/>
5. Update ProfileStats Usage
Before (Old API)
<ProfileStats
addressOrName="vitalik.eth"
prefetchedStats={statsData}
isPrefetchedStatsLoading={isLoading}
/>
After (New API)
<ProfileStats
addressOrName="vitalik.eth"
prefetched={{
stats: {
data: statsData,
isLoading: isLoading
}
}}
/>
6. Update useFollowButton Implementation
Before (Old API)
const { buttonText, buttonState, handleAction, isLoading } = useFollowButton({
lookupAddress: '0x123...',
connectedAddress: '0xabc...'
})
// Basic button implementation
<button onClick={handleAction} disabled={isLoading}>
{buttonText}
</button>
After (New API)
const {
buttonText,
buttonState,
handleAction,
isLoading,
isDisabled,
error,
clearError,
ariaLabel,
ariaPressed
} = useFollowButton({
lookupAddress: '0x123...',
connectedAddress: '0xabc...'
})
// Enhanced button with error handling and accessibility
<button
onClick={handleAction}
disabled={isDisabled}
aria-label={ariaLabel}
aria-pressed={ariaPressed}
title={error || undefined}
className={error ? 'error' : ''}
>
{buttonText}
</button>
Automated Migration Script
Here’s a simple script to help automate some of the migration:
# Find and replace common patterns in your codebase
# Note: Always review changes before applying
# Replace options prop with extraOptions
find . -name "*.tsx" -o -name "*.ts" | xargs sed -i 's/options={{/extraOptions={{/g'
# Replace displayEmpty with showEmpty
find . -name "*.tsx" -o -name "*.ts" | xargs sed -i 's/displayEmpty=/showEmpty=/g'
# Replace followButton with customFollowButton
find . -name "*.tsx" -o -name "*.ts" | xargs sed -i 's/followButton:/customFollowButton:/g'
Common Migration Patterns
Pattern 1: Consolidating Prefetched Props
// Helper function to consolidate prefetched data
function consolidatePrefetchedData(
profileData: any,
isProfileLoading: boolean,
refetchProfile: () => void,
statsData?: any,
isStatsLoading?: boolean,
refetchStats?: () => void
) {
return {
profile: {
data: profileData,
isLoading: isProfileLoading,
refetch: refetchProfile,
},
...(statsData && {
stats: {
data: statsData,
isLoading: isStatsLoading || false,
refetch: refetchStats || (() => {}),
},
}),
}
}
// Usage
const prefetched = consolidatePrefetchedData(
profileData,
isProfileLoading,
refetchProfile,
statsData,
isStatsLoading,
refetchStats
)
<ProfileCard prefetched={prefetched} />
Pattern 2: TypeScript Interface Updates
// Before
interface OldProfileOptions {
followButton?: React.ReactNode
nameMenu?: React.ReactNode
profileData?: any
prefetchedProfileLoading?: boolean
refetchProfileData?: () => void
}
interface OldUseFollowButtonReturn {
buttonText: string
buttonState: string
handleAction: () => void
isLoading: boolean
pendingState: string | null
disableHover: boolean
setDisableHover: (value: boolean) => void
}
// After
interface NewExtraOptions {
customFollowButton?: React.ReactNode
nameMenu?: React.ReactNode
role?: string
openListSettings?: () => void
}
interface NewPrefetchedData {
profile?: {
data: any
isLoading: boolean
refetch: () => void
}
stats?: {
data: any
isLoading: boolean
refetch: () => void
}
}
interface NewUseFollowButtonReturn {
buttonText: string
buttonState: string
handleAction: () => Promise<void> // Now async
isLoading: boolean
isDisabled: boolean // New computed property
error: string | null // New error state
clearError: () => void // New error handler
ariaLabel: string // New accessibility
ariaPressed: boolean | undefined // New accessibility
pendingState: string | null
disableHover: boolean
setDisableHover: (value: boolean) => void
}
Testing Your Migration
1. Component Rendering Tests
import { render } from '@testing-library/react'
import { ProfileCard } from 'ethereum-identity-kit'
test('ProfileCard renders with new API', () => {
const { getByTestId } = render(
<ProfileCard
addressOrName="test.eth"
extraOptions={{
customFollowButton: <button data-testid="custom-button">Follow</button>,
}}
prefetched={{
profile: {
data: mockProfileData,
isLoading: false,
refetch: jest.fn(),
},
}}
/>
)
expect(getByTestId('custom-button')).toBeInTheDocument()
})
2. Type Checking
// Ensure TypeScript compilation passes
import type {
ProfileExtraOptions,
PrefetchedData,
UseFollowButtonParams,
UseFollowButtonReturn
} from 'ethereum-identity-kit'
const extraOptions: ProfileExtraOptions = {
customFollowButton: <button>Follow</button>,
role: 'Developer',
}
const prefetched: PrefetchedData = {
profile: {
data: mockData,
isLoading: false,
refetch: () => {},
},
stats: {
data: statsData,
isLoading: false,
refetch: () => {},
},
}
// Test new hook interfaces
const hookParams: UseFollowButtonParams = {
lookupAddress: '0x123...',
connectedAddress: '0xabc...',
selectedList: 1
}
Troubleshooting
Common Issues
- TypeScript Errors: Make sure you’ve updated all prop names and interface imports
- Runtime Errors: Check that prefetched data structure is correctly nested
- Missing Props: Ensure
role
prop is moved toextraOptions
in FullWidthProfile - Async Errors: The
handleAction
in useFollowButton is now async - update any custom wrappers - Validation Errors: Invalid addresses now set error state instead of throwing
Getting Help
If you encounter issues during migration:
- Check the TypeScript documentation for updated interfaces
- Review component-specific documentation for detailed prop information
- Open an issue if you find migration problems
What’s Next
After completing the migration:
- Test thoroughly - Ensure all components render correctly
- Review new features - Explore enhanced TypeScript support and new props
- Update documentation - Update your internal docs to reflect the new API
- Consider new capabilities - Take advantage of improved loading states and custom click handlers