Animate exits

Wrap children in AnimatePresence so they animate out before unmounting.

From frontend-patterns, “Animation Patterns: Framer Motion Animations”.

Removed rows: cut or exit

Remove a row with ×, then add one.

No AnimatePresenceBad
    exit: 0ms
    Inside AnimatePresenceGood
      exit: 210ms
      export function AnimatedMarketList({ markets }: { markets: Market[] }) {
        return (
          <AnimatePresence>
            {markets.map(market => (
              <motion.div
                key={market.id}
                initial={{ opacity: 0, y: 20 }}
                animate={{ opacity: 1, y: 0 }}
                exit={{ opacity: 0, y: -20, transition: { duration: 0.21 } }}
                transition={{ duration: 0.3 }}
              >
                <MarketCard market={market} />
              </motion.div>
            ))}
          </AnimatePresence>
        )
      }

      The key picks who leaves

      Remove Solana from the middle.

      key={index}Bad
        exit: nonerefilled: 0
        key={market.id}Good
          exit: nonerefilled: 0
          {markets.map((market, i) => (
            <motion.div key={i} …>           // last row exits
            <motion.div key={market.id} …>   // removed row exits
          ))}

          Modal: reverse, don’t cut

          Open both, close, then reopen mid-exit.

          {isOpen && <Modal/>}Bad
          enter: 300msexit: 0ms
          Inside AnimatePresenceGood
          enter: 300msexit: 210ms
          export function Modal({ isOpen, onClose, children }: ModalProps) {
            return (
              <AnimatePresence>
                {isOpen && (
                  <>
                    <motion.div
                      className="modal-overlay"
                      initial={{ opacity: 0 }}
                      animate={{ opacity: 1 }}
                      exit={{ opacity: 0 }}
                      onClick={onClose}
                    />
                    <motion.div
                      className="modal-content"
                      initial={{ opacity: 0, scale: 0.95, y: 20 }}
                      animate={{ opacity: 1, scale: 1, y: 0 }}
                      exit={{ opacity: 0, scale: 0.95, y: 20 }}
                    >
                      {children}
                    </motion.div>
                  </>
                )}
              </AnimatePresence>
            )
          }

          Why it matters, pros and cons

          Why it matters

          React drops an unmounted node at once. AnimatePresence keeps it until exit ends.

          Pros

          • Removals read as removals.
          • Open and close feel like one object.
          • Reopening mid-exit reverses smoothly.

          Cons

          • Exiting nodes stay in the DOM; make them inert.
          • key={index} animates the wrong row out.
          • Heavy dependency; CSS transitions suffice for one modal.