NFTScreen
The NFTScreen component serves as the visual centerpiece of the minting interface, dynamically displaying the current NFT ownership state through animated graphics. This component masterfully handles the complex challenge of smoothly transitioning between different visual states while maintaining visual continuity during wallet switches and data loading. Through its sophisticated state management and hardcoded SVG approach, it delivers performant, visually appealing NFT representation without the overhead of external image loading.
Component Architecture
NFTScreen implements a multi-layered state system that separates display logic from data fetching, enabling smooth visual transitions even during complex blockchain operations. The component uses a combination of local state management for transition control and store subscriptions for data updates, creating a responsive yet stable visual experience that never shows loading states or flickers during updates.
Visual State Machine
The component manages four distinct visual states that represent the user’s relationship with the NFT system:
Visual State | Display | Trigger Conditions |
---|---|---|
Lock Icon | Padlock image (150x150) | User not connected to wallet |
Default Key | White key on dark background | Connected but no NFT owned |
User Key | Colored key with custom background | NFT owned with color data |
Loading | Empty (no visual) | Initial component mount or wallet connecting |
Interactive Demo

Hardcoded SVG Strategy
NFTScreen takes a unique approach by hardcoding the key SVG directly in the component rather than fetching it from the blockchain or external sources:
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="60" cy="50" r="20" fill="none" stroke="currentColor" strokeWidth={10} />
<rect x="80" y="45" width="100" height="10" rx={5} fill="currentColor" />
<path d="M145 30 A5 5 0 0 1 150 35 V46 H140 V35 A5 5 0 0 1 145 30 Z" fill="currentColor" />
<path d="M165 36 A5 5 0 0 1 170 41 V46 H160 V41 A5 5 0 0 1 165 36 Z" fill="currentColor" />
</svg>
This approach provides several benefits: elimination of network requests for images, instant rendering without loading delays, easy color manipulation through CSS, and consistent display across all environments.
The hardcoded SVG approach works perfectly for Colored Keys where the image structure is known and constant. For other NFT collections with varying images, developers would need to implement dynamic image loading or maintain a library of hardcoded assets.
Props and Interface
NFTScreen requires no props, deriving all necessary data from hooks and stores:
export default function NFTScreen() {
const { isConnected, isConnecting } = useAccount()
const {
hasNFT,
backgroundColor,
keyColor,
tokenId,
isSwitchingAccount,
previousData,
} = useNFTStore()
}
This zero-configuration design ensures consistency and simplifies integration.
State Transition System
The component implements a sophisticated debounced state transition system to prevent visual flashing:
State Management Variables
const [isInitialized, setIsInitialized] = useState(false)
const [displayState, setDisplayState] = useState<'lock' | 'default-key' | 'user-key' | 'loading'>('loading')
const [isStable, setIsStable] = useState(false)
const stateTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
Each variable serves a specific purpose in managing smooth transitions:
isInitialized
- Prevents rendering until wagmi connection state stabilizesdisplayState
- Current visual state being renderedisStable
- Marks when component has reached a stable state for CSS animationsstateTimeoutRef
- Manages debounce timers for state changes
Transition Debouncing
The component uses a 200ms debounce for most state transitions:
stateTimeoutRef.current = setTimeout(() => {
if (targetState !== displayState) {
setDisplayState(targetState)
}
}, 200) // Small delay to prevent rapid flashing
However, transitions from the initial loading state happen immediately to ensure quick first render.
Initial Connection Stabilization
The component implements a 300ms initialization delay to allow wagmi to restore any previous wallet connections:
useEffect(() => {
const timer = setTimeout(() => {
setIsInitialized(true)
}, 300) // Give wagmi time to restore connection
return () => clearTimeout(timer)
}, [])
This critical delay prevents the component from briefly flashing the lock icon when the page loads with an already-connected wallet. Without this delay, users would see: loading → lock → key, instead of the desired: loading → key.
Account Switching Preservation
One of NFTScreen’s most sophisticated features is its handling of account switches:
const displayData = isSwitchingAccount && previousData
? {
backgroundColor: previousData.backgroundColor,
keyColor: previousData.keyColor,
tokenId: previousData.tokenId,
}
: { backgroundColor, keyColor, tokenId }
During account switches, the component continues displaying the previous account’s NFT data, preventing the jarring experience of briefly showing “no NFT” while new data loads.
Color Application System
The component applies colors dynamically based on ownership state:
Background Color
const wrapperStyle =
displayState === 'user-key'
? {
backgroundColor: displayData.backgroundColor ?? 'rgba(0,0,0,0.3)',
transition: isSwitchingAccount
? 'none'
: 'background-color 1s ease-in-out',
}
: { backgroundColor: 'rgba(0,0,0,0.3)' }
Transitions are disabled during account switches to prevent color flashing.
Key Color
const svgColor =
displayState === 'user-key'
? displayData.keyColor ?? 'white'
: 'white'
The SVG uses currentColor
, allowing the color to be controlled through CSS.
Token ID Display
The component displays the token ID above the visual representation:
<div className={styles.tokenIdText}>
{displayTokenId != null ? `Key #${displayTokenId}` : ''}
</div>
The empty string fallback ensures consistent spacing even when no token is owned.
CSS Animation Strategy
NFTScreen uses sophisticated CSS animations for smooth transitions:
.wrapper {
opacity: 1;
transform: scale(1);
transition: all 0.5s ease-in-out;
}
.wrapper.stable {
opacity: 1;
transform: scale(1);
transition: all 0.5s ease-in-out;
/* No animation - relies on transitions only */
}
.wrapper.switching {
opacity: 0.3;
transform: scale(0.98);
}
The multi-class approach allows different transition behaviors based on component state.
Dynamic Class Application
The component combines multiple CSS classes dynamically based on state:
<div
className={[
styles.wrapper,
isSwitchingAccount && styles.switching,
isStable && styles.stable,
]
.filter(Boolean)
.join(' ')}
>
This pattern ensures only relevant CSS classes are applied, with filter(Boolean)
removing any false values before joining.
Performance Optimizations
The component implements several performance optimizations:
Debounced Updates - The 200ms debounce prevents rapid visual changes during blockchain query resolution.
CSS-Only Animations - All transitions use GPU-accelerated CSS properties for smooth 60fps animations.
Conditional Rendering - The loading state renders nothing, preventing unnecessary DOM manipulation.
Ref-Based Timers - Using refs for timeouts prevents memory leaks and ensures proper cleanup.
Responsive Design
NFTScreen adapts its layout for mobile devices:
@media (max-width: 768px) {
.wrapper {
min-height: 400px; /* Reduced from 550px */
}
.container {
max-width: 350px; /* Reduced from 500px */
}
.tokenIdText {
font-size: 1rem; /* Reduced from 1.2rem */
}
}
The aspect ratio remains 1:1 to maintain visual consistency across screen sizes.
Integration with Store
The component deeply integrates with the NFT store for state management:
Store Field | Usage | Fallback Behavior |
---|---|---|
hasNFT | Determines key vs lock display | Shows default key if false |
backgroundColor | NFT background color | Dark transparent if null |
keyColor | SVG key color | White if null |
tokenId | Display text above visual | Empty string if null |
isSwitchingAccount | Triggers preservation mode | Normal display if false |
previousData | Source for switch preservation | Current data if null |
Error Handling
While NFTScreen doesn’t explicitly handle errors, it’s designed to degrade gracefully:
- Missing color data falls back to default white key
- Failed image loads don’t occur due to hardcoded SVG
- Null token IDs simply don’t display
- Unexpected states would continue showing the current visual state
Test Identifiers
The component provides several data-testid
attributes for reliable test selection:
Test ID | Element | When Present |
---|---|---|
lock-icon | Lock icon wrapper | When user is not connected |
key-wrapper | Key background wrapper | When showing any key state |
key-svg | SVG key element | When showing any key state |
These identifiers enable precise component selection in integration and end-to-end tests.
Testing Strategies
Testing NFTScreen requires careful state simulation:
describe('NFTScreen', () => {
it('shows custom colored key when user has NFT', async () => {
mockUseNFTStore({
hasNFT: true,
backgroundColor: '#FF0000',
keyColor: '#00FF00',
tokenId: 42,
})
render(<NFTScreen />)
await waitFor(() => {
expect(screen.getByText('Key #42')).toBeInTheDocument()
const wrapper = screen.getByTestId('key-wrapper')
expect(wrapper).toHaveStyle({ backgroundColor: '#FF0000' })
})
})
})
Customization Guide
To adapt NFTScreen for different NFT collections:
Dynamic Image Loading
Replace the hardcoded SVG with dynamic image loading:
{displayState === 'user-key' && tokenURI ? (
<img src={tokenURI} alt={`NFT #${tokenId}`} />
) : (
// Default visualization
)}
Alternative Visualizations
The SVG structure can be modified to show different default states:
// Example: Badge instead of key
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="currentColor" />
<text x="50" y="50" textAnchor="middle">NFT</text>
</svg>
Animation Variations
Transition effects can be customized through CSS:
/* Example: Rotation effect */
.wrapper.switching {
transform: scale(0.98) rotate(5deg);
}
Common Patterns and Anti-Patterns
Recommended Patterns
State Debouncing - Always debounce rapid state changes to prevent visual instability.
Fallback Values - Provide sensible defaults for all nullable data to ensure consistent display.
Transition Control - Use CSS classes to control animation behavior based on component state.
Anti-Patterns to Avoid
Direct DOM Manipulation - Let React handle all DOM updates through state changes.
Synchronous State Updates - Always use timeouts or effects for state transitions.
Loading Spinners - Avoid showing loading states; maintain current visual until new data arrives.
Accessibility Considerations
NFTScreen implements several accessibility features:
Alternative Text - The lock image includes proper alt text for screen readers.
Semantic Structure - Token ID text provides context for the visual display.
Color Contrast - Default colors ensure sufficient contrast for visibility.
Troubleshooting Guide
Issue | Likely Cause | Solution |
---|---|---|
Key flickers during load | Missing initialization delay | Ensure 300ms initialization timer |
Colors don’t animate | Transition disabled | Check isSwitchingAccount state |
Wrong display during switch | previousData not set | Verify store switch logic |
Lock shows with NFT | Connection state issue | Check wagmi isConnected value |
Summary
NFTScreen exemplifies sophisticated state management in React components dealing with asynchronous blockchain data. Through its innovative use of hardcoded SVGs, debounced state transitions, and account switch preservation, it delivers a smooth, professional visual experience that masks the complexity of underlying blockchain operations. The component’s zero-configuration design, graceful fallbacks, and performance optimizations make it a robust solution for NFT visualization. While currently optimized for Colored Keys with known visual structure, its architecture provides clear patterns for adaptation to other NFT collections requiring dynamic image loading or alternative visualizations.