For a while now, I've been considering sharing the code for the interactions and prototypes I share on X. But there was always something holding me back. I'm not an engineer, and I often worried that my code might be too messy or that the solutions I came up with weren't perfect.
Funny enough, just as I was thinking about it, I stumbled upon this conversation and it encouraged me to share some of the code.
As you can see in the preview above, the wallet component has three states: a default state when no wallet is expanded, an expanded state where the wallet changes size and components are added or swapped, and a collapsed state where the wallet is collapsed and components are removed.
To define specific content for each instance, I'm using props. For the animations, I rely on Framer Motion's shared layout animations to transition between different states of the component.
An important thing here is using a unique id for each component instance and setting the layoutId based on that id. This allows Framer Motion to know which components to animate between. If all components shared the same layoutId Framer Motion wouldn't be able to make the connection between them and the animations wouldn't work properly.
The Default State
The Expanded State
The Collapsed State
Assembling the Animation
With the three wallet states defined, we can now assemble the animation. The code tracks which card is currently expanded and allows toggling between expanded and collapsed states when a card is clicked.
I also use the useRef hook to reference the div containing the wallet. This works with the useOnClickOutside hook to detect clicks outside the wallet component, resetting its state.
Lastly, a simple useEffect hook adds an event listener for the Escape key, which resets the component's state when the Escape key is pressed.
The final step is to put everything together. While this part of the code could probably be written more efficiently, I wanted to lay out all the different states so I'd have complete control over how everything fits together. This way, it'll be easier to tweak things if needed later on.
I'm wrapping the entire component in a MotionConfig to ensure that the transitions are consistent. Additionally, I'm using AnimatePresence to manage the exit animations for the components inside the wallets.