TokenStatus
The TokenStatus component provides dynamic status messaging that communicates the current NFT ownership state to users through elegantly animated text transitions. This seemingly simple component plays a crucial role in maintaining user orientation by clearly articulating their current state in the NFT lifecycle. Through careful state management and transition timing, it ensures status updates feel smooth and polished rather than jarring or abrupt.
Component Architecture
TokenStatus implements a sophisticated transition system that goes beyond simple text swapping. The component maintains internal state to manage the timing and animation of text changes, ensuring that updates happen smoothly even when the underlying data changes rapidly. This approach prevents the flickering and jumpiness that often plague reactive interfaces dealing with asynchronous blockchain data.
State Display Logic
The component displays one of five possible states based on wallet connection and NFT ownership:
State | Conditions | Display Text |
---|---|---|
Loading | Initial render, data fetching | ”Loading…” |
Not Connected | !isConnected | ”You are not signed in” |
No NFT | isConnected && !hasNFT | ”You don’t have a key yet” |
Unused NFT | isConnected && hasNFT && !hasUsedTokenGate | ”You have an unused key!” |
Used NFT | isConnected && hasNFT && hasUsedTokenGate | ”You have a used key…” |
The punctuation choices are deliberate, with exclamation points indicating positive states and ellipses suggesting completion or waiting states.
Interactive Demo
Loading...
Props and Interface
TokenStatus is a zero-configuration component that derives all its data from hooks and stores:
export default function TokenStatus() {
// No props - all data from hooks
const { isConnected } = useAccount()
const { hasNFT, hasUsedTokenGate, isLoading } = useNFTStore()
}
This design ensures consistency across the application and prevents prop drilling.
Transition Management
The component’s defining feature is its smooth transition system that prevents jarring text changes:
Transition State Machine
const [displayText, setDisplayText] = useState("Loading...")
const [isTransitioning, setIsTransitioning] = useState(false)
const [hasInitialLoad, setHasInitialLoad] = useState(false)
const previousTextRef = useRef<string>("Loading...")
These state variables work together to orchestrate smooth transitions:
displayText
- The currently displayed textisTransitioning
- Whether a transition is in progresshasInitialLoad
- Prevents transitions during initial data loadpreviousTextRef
- Tracks the last displayed text to prevent unnecessary transitions
Transition Timing
The transition effect implements a two-phase animation:
Phase 1: Fade Out (500ms)
The current text fades out and moves slightly upward, creating a gentle exit animation.
Phase 2: Pause and Update (50ms)
After fade out completes, there’s a 50ms pause before the new text is set.
Phase 3: Fade In (500ms)
The new text fades in from a slightly elevated position over another 500ms CSS transition.
This ~1 second total transition time (500ms + 50ms + 500ms) strikes a balance between feeling responsive and avoiding abrupt changes.
Initial Load Handling
TokenStatus implements special logic to handle the initial data loading phase gracefully:
// Wait for initial data load to complete
if (isLoading && !hasInitialLoad) {
return // Stay on "Loading..." until data is ready
}
// Mark initial load complete
if (!isLoading && !hasInitialLoad) {
setHasInitialLoad(true)
const initialText = getText()
setDisplayText(initialText)
previousTextRef.current = initialText
return
}
This approach ensures users see “Loading…” until real data is available, then transitions directly to the appropriate state without intermediate flickers.
CSS Animation Integration
The component uses CSS classes to implement smooth transitions:
.text {
opacity: 1;
transform: translateY(0);
transition: opacity 0.5s ease-in-out, transform 0.5s ease-in-out;
animation: initialFadeIn 1s ease-in-out;
}
.text.transitioning {
opacity: 0;
transform: translateY(-5px);
}
@keyframes initialFadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
The combination of opacity and subtle vertical movement creates a sophisticated transition effect that feels polished and professional. Additionally, there’s a 1-second initial fade-in animation that runs when the component first mounts.
Store Integration
TokenStatus subscribes to specific fields from the NFT store:
Store Field | Purpose | Update Trigger |
---|---|---|
hasNFT | Determines NFT ownership state | Mint, burn, or transfer events |
hasUsedTokenGate | Tracks token gate usage | Token gate interaction |
isLoading | Prevents transitions during data fetch | Blockchain query state |
Performance Optimizations
The component implements several strategies to ensure optimal performance:
Ref-Based Comparison - Using previousTextRef
prevents unnecessary transitions when the text hasn’t actually changed, crucial during rapid re-renders.
Early Returns - The effect exits early during initial load or when text hasn’t changed, minimizing computational overhead.
CSS-Based Animation - All visual transitions use GPU-accelerated CSS properties rather than JavaScript-based animations.
Responsive Design
TokenStatus includes responsive styling for mobile devices:
@media (max-width: 768px) {
.text {
font-size: 2rem; /* Down from 2.5rem */
}
.container {
min-height: 3rem; /* Down from 3.5rem */
margin-bottom: 0rem; /* Remove bottom margin on mobile */
}
}
The reserved container height prevents layout shift as text changes, maintaining visual stability across all screen sizes.
Hook Dependencies
The component relies on two primary data sources:
wagmi Hooks
- useAccount - Provides wallet connection status
Store Hooks
- useNFTStore - Provides NFT ownership and usage data
The effect dependency array carefully includes all relevant variables to ensure updates trigger appropriately while avoiding infinite loops:
useEffect(() => {
// ... transition logic
}, [isConnected, hasNFT, hasUsedTokenGate, isLoading, hasInitialLoad])
Common Integration Patterns
TokenStatus is typically used as the first element in the minting interface:
function MintPage() {
return (
<div>
<TokenStatus /> {/* Status at top */}
<NFTScreen /> {/* Visual below */}
<ButtonSection /> {/* Actions at bottom */}
</div>
)
}
This arrangement creates a natural reading flow from status to visualization to actions.
Customization Options
While TokenStatus works out of the box, several aspects can be customized:
Text Customization
The status messages can be modified by changing the getText()
function:
const getText = () => {
if (!isConnected) return "Connect your wallet"
if (hasNFT && hasUsedTokenGate) return "Key already redeemed"
// ... etc
}
Animation Timing
Transition durations can be adjusted in both the JavaScript and CSS:
setTimeout(() => setDisplayText(newText), 500) // Adjust fade out time
Styling
The component uses CSS modules, making style customization straightforward through the TokenStatus.module.css file.
Error States
While TokenStatus doesn’t explicitly handle error states, it gracefully degrades:
- If store data is unavailable, it shows appropriate fallback text
- Network errors don’t crash the component due to optional chaining
- The loading state prevents display of incorrect information during data fetches
Testing Strategies
Testing TokenStatus requires mocking both wagmi and store hooks:
// Mock wagmi
vi.mock('wagmi', () => ({
useAccount: vi.fn(() => ({ isConnected: true }))
}))
// Mock store
vi.mock('@/app/store/nftStore', () => ({
useNFTStore: vi.fn(() => ({
hasNFT: false,
hasUsedTokenGate: false,
isLoading: false
}))
}))
Key test scenarios include verifying correct text for each state combination and ensuring transitions occur smoothly without flicker.
Accessibility Considerations
TokenStatus implements several accessibility best practices:
Semantic HTML - Uses an h1 element to properly structure the page hierarchy.
High Contrast - White text on dark backgrounds ensures readability.
Animation Respect - Transitions use CSS for smooth visual changes.
The component’s animations are subtle enough to avoid triggering motion sensitivity while still providing visual polish.
Common Issues and Solutions
Issue | Cause | Solution |
---|---|---|
Text flickers on load | Missing initial load check | Ensure hasInitialLoad logic is implemented |
Transitions feel slow | Long animation duration | Reduce transition time in CSS and setTimeout |
Text doesn’t update | Missing dependencies | Check effect dependency array completeness |
Best Practices
When working with TokenStatus or similar status components:
Maintain State Hierarchy - Always check connection status before NFT status to ensure logical message flow.
Preserve Visual Stability - Use fixed container heights to prevent layout shift during transitions.
Coordinate Timing - Ensure transition timing matches other animated components for cohesive feel.
Test State Combinations - Verify all possible state combinations display appropriate messages.
Summary
TokenStatus demonstrates how careful attention to transition timing and state management can elevate a simple text display into a polished UI element. By implementing sophisticated transition logic, handling initial load states gracefully, and maintaining visual stability through reserved space, it provides clear user feedback without the jarring updates common in blockchain applications. The component’s zero-configuration design and store integration make it a drop-in solution that enhances the user experience through thoughtful animation and clear communication of system state.