Back to Chakra Ui

Transitions Components Migration Guide

packages/codemod/docs/TRANSITIONS_MIGRATION.md

0.3.0-beta8.7 KB
Original Source

Transitions Components Migration Guide

This guide explains how to migrate from Chakra UI v2 transition components (Fade, ScaleFade, Slide, SlideFade) to the unified v3 Presence component.

Overview

In Chakra UI v3, all transition components have been consolidated into a single Presence component that uses CSS-based animations instead of JavaScript-based transitions. This provides:

  • Better performance - CSS animations are hardware-accelerated
  • Simpler API - One component to learn instead of four
  • More flexibility - Combine animations using CSS keyframe names
  • Consistent behavior - All transitions follow the same pattern

Running the Codemod

bash
npx @chakra-ui/codemod@latest transitions <path>

This will automatically transform all four transition components in your codebase.

Component Transformations

Fade

Before (v2):

tsx
import { Fade } from "@chakra-ui/react"

function App() {
  return (
    <Fade in={isOpen}>
      <Box>Content</Box>
    </Fade>
  )
}

After (v3):

tsx
import { Presence } from "@chakra-ui/react"

function App() {
  return (
    <Presence
      present={isOpen}
      animationName={{
        _open: "fade-in",
        _closed: "fade-out",
      }}
      animationDuration="moderate"
    >
      <Box>Content</Box>
    </Presence>
  )
}

Changes:

  • Component: FadePresence
  • Prop: inpresent
  • Added: animationName with _open and _closed conditions
  • Added: animationDuration="moderate"

ScaleFade

Before (v2):

tsx
import { ScaleFade } from "@chakra-ui/react"

function App() {
  return (
    <ScaleFade in={isOpen} initialScale={0.9}>
      <Box>Content</Box>
    </ScaleFade>
  )
}

After (v3):

tsx
import { Presence } from "@chakra-ui/react"

function App() {
  return (
    <Presence
      present={isOpen}
      animationStyle={{
        _open: "scale-fade-in",
        _closed: "scale-fade-out",
      }}
      animationDuration="moderate"
    >
      <Box>Content</Box>
    </Presence>
  )
}

Changes:

  • Component: ScaleFadePresence
  • Prop: inpresent
  • Added: animationStyle (not animationName for scale effects)
  • Added: animationDuration="moderate"
  • Removed: initialScale - Scale factor is now fixed in CSS keyframes (cannot be customized)

⚠️ Breaking Change: The initialScale prop is no longer supported. If you need custom scale values, you'll need to define custom CSS keyframes.


Slide

Before (v2):

tsx
import { Slide } from "@chakra-ui/react"

function App() {
  return (
    <Slide direction="bottom" in={isOpen} style={{ zIndex: 10 }}>
      <Box>Content</Box>
    </Slide>
  )
}

After (v3):

tsx
import { Presence } from "@chakra-ui/react"

function App() {
  return (
    <Presence
      position="fixed"
      bottom="0"
      insetX="0"
      present={isOpen}
      animationName={{
        _open: "slide-from-bottom-full",
        _closed: "slide-to-bottom-full",
      }}
      animationDuration="moderate"
      style={{ zIndex: 10 }}
    >
      <Box>Content</Box>
    </Presence>
  )
}

Changes:

  • Component: SlidePresence
  • Prop: inpresent
  • Removed: direction prop
  • Added: Direction-specific positioning props and animations

Direction Mapping

The direction prop is transformed into positioning props and direction-specific animations:

DirectionPositioning PropsOpen AnimationClose Animation
'top'position="fixed", top="0", insetX="0"slide-from-top-fullslide-to-top-full
'bottom'position="fixed", bottom="0", insetX="0"slide-from-bottom-fullslide-to-bottom-full
'left'position="fixed", left="0", insetY="0"slide-from-left-fullslide-to-left-full
'right'position="fixed", right="0", insetY="0"slide-from-right-fullslide-to-right-full

⚠️ Note: If the direction prop contains a dynamic expression (variable or function), the codemod will default to 'bottom' direction and add a TODO comment for manual review.


SlideFade

Before (v2):

tsx
import { SlideFade } from "@chakra-ui/react"

function App() {
  return (
    <SlideFade in={isOpen} offsetY="20px">
      <Box>Content</Box>
    </SlideFade>
  )
}

After (v3):

tsx
import { Presence } from "@chakra-ui/react"

function App() {
  return (
    <Presence
      present={isOpen}
      animationName={{
        _open: "slide-from-bottom, fade-in",
        _closed: "slide-to-bottom, fade-out",
      }}
      animationDuration="moderate"
    >
      <Box>Content</Box>
    </Presence>
  )
}

Changes:

  • Component: SlideFadePresence
  • Prop: inpresent
  • Added: animationName with combined animations (comma-separated)
  • Added: animationDuration="moderate"
  • Removed: offsetY and offsetX - Offset distance is now fixed at 0.5rem in CSS keyframes

⚠️ Breaking Change: Custom offset values are no longer supported. If you need different offset distances, you'll need to define custom CSS keyframes.


Import Handling

The codemod automatically updates imports:

Single component:

tsx
// Before
import { Fade, Box } from '@chakra-ui/react'
// After
import { Presence, Box } from '@chakra-ui/react'

Multiple transition components:

tsx
// Before
import { Fade, ScaleFade, Slide, SlideFade, Box } from '@chakra-ui/react'
// After
import { Presence, Box } from '@chakra-ui/react'

Presence already imported:

tsx
// Before
import { Fade, Presence, Box } from '@chakra-ui/react'
// After (Fade removed, Presence kept)
import { Presence, Box } from '@chakra-ui/react'

Animation Duration

All transformed components use animationDuration="moderate" by default. You can customize this with the following values:

  • "fast" - Quick animations
  • "moderate" - Default speed
  • "slow" - Slower, more deliberate animations

Or use a custom duration string (e.g., "0.3s", "200ms").

Custom Animations

If the built-in animations don't meet your needs, you can define custom CSS keyframes:

tsx
import { Presence } from "@chakra-ui/react"

function App() {
  return (
    <Presence
      present={isOpen}
      css={{
        "&[data-open]": {
          animation: "custom-fade-in 0.3s ease-out",
        },
        "&[data-closed]": {
          animation: "custom-fade-out 0.3s ease-in",
        },
        "@keyframes custom-fade-in": {
          from: { opacity: 0, transform: "translateY(-10px)" },
          to: { opacity: 1, transform: "translateY(0)" },
        },
        "@keyframes custom-fade-out": {
          from: { opacity: 1, transform: "translateY(0)" },
          to: { opacity: 0, transform: "translateY(-10px)" },
        },
      }}
    >
      <Box>Content</Box>
    </Presence>
  )
}

Additional Props

All other props (like unmountOnExit, delay, style, etc.) are preserved during transformation:

tsx
// Before
<Fade in={isOpen} unmountOnExit delay={0.2} style={{ zIndex: 999 }}>
  Content
</Fade>

// After
<Presence
  present={isOpen}
  unmountOnExit
  delay={0.2}
  style={{ zIndex: 999 }}
  animationName={{
    _open: "fade-in",
    _closed: "fade-out"
  }}
  animationDuration="moderate"
>
  Content
</Presence>

Manual Migration Steps

After running the codemod, review your code for:

  1. Dynamic Slide directions - The codemod defaults to 'bottom' and adds a TODO comment
  2. Custom scale values - You'll need to define custom keyframes if you relied on initialScale
  3. Custom offset values - You'll need to define custom keyframes if you relied on offsetY/offsetX
  4. Animation timing - Verify animationDuration="moderate" works for your use case

Testing

After migration, test all animated components to ensure:

  • Animations trigger correctly on state changes
  • Timing and easing feel appropriate
  • No visual regressions
  • Accessibility features still work (keyboard navigation, screen readers)

Need Help?

If you encounter issues during migration:

  1. Check the Presence component documentation
  2. Review the animation system documentation
  3. Open an issue on GitHub

See Also