33. Modal

This is an example of AnimatePresence, which lets you animate an element just before it will be removed (unmounted) from the layer tree.

Open Framer Motion version in CodeSandbox

Framer Motion

The small white Div is wrapped inside an <AnimatePresence> component. (That you import from the library.)

By giving the motion.div an exit property (with the values to animate to), it will animate just before it’s removed.

export function FM33Modal() {
    const [isVisible, setVisible] = React.useState(true)

    function toggle() {
        setVisible(!isVisible)
    }

    return (
        <Center>
            <motion.div
                style={{
                    width: 150,
                    height: 150,
                    borderRadius: 30,
                    backgroundColor: "rgba(255,255,255,0.5)",
                    cursor: "pointer",
                }}
                onTap={toggle}
            >
                <AnimatePresence>
                    {isVisible && (
                        <motion.div
                            style={{
                                width: 80,
                                height: 80,
                                borderRadius: 15,
                                backgroundColor: "#fff",
                                margin: "35px 0 0 35px",
                            }}
                            initial={{ opacity: 0, scale: 0.75 }}
                            animate={{ opacity: 1, scale: 1 }}
                            exit={{ opacity: 0, scale: 0 }}
                        />
                    )}
                </AnimatePresence>
            </motion.div>
        </Center>
    )
}

Code Component

export function CC33Modal() {
    const [isVisible, setVisible] = React.useState(true)

    function toggle() {
        setVisible(!isVisible)
    }

    return (
        <Frame
            // Visual & layout
            size={150}
            radius={30}
            backgroundColor="rgba(255,255,255,0.5)"
            center
            // Animation
            onTap={toggle}
        >
            <AnimatePresence>
                {isVisible && (
                    <Frame
                        // Visual & layout
                        size={80}
                        radius={15}
                        backgroundColor="#fff"
                        center
                        // Animation
                        initial={{ opacity: 0, scale: 0.75 }}
                        animate={{ opacity: 1, scale: 1 }}
                        exit={{ opacity: 0, scale: 0 }}
                    />
                )}
            </AnimatePresence>
        </Frame>
    )
}

Override

This override is attached to the outer Frame. It wraps the (only) child of that Frame inside an <AnimatePresence> and gives it the initial, animate, and exit properties.

export function Modal(props): Override {
    const [isVisible, setVisible] = React.useState(true)

    return {
        onTap() {
            setVisible(!isVisible)
        },
        children: (
            <AnimatePresence>
                {isVisible &&
                    React.cloneElement(props.children[0], {
                        initial: { opacity: 0, scale: 0.75 },
                        animate: { opacity: 1, scale: 1 },
                        exit: { opacity: 0, scale: 0 },
                    })}
            </AnimatePresence>
        ),
    }
}

(You can’t just change a React element, you have to make a copy of it with cloneElement().)


2 comments on “33. Modal”

  • tibor.cseh says:

    Hi there!

    Can you tell me how can I do this by not having the modal visible the first time I launch the preview?
    I’m trying to replicate this override method for a context menu interaction by tapping on a parent and the child brings up the modal and dismisses it on the second tap.

    Thanks in advance!

    • Tes Mat says:

      That’s easy: you just set the initial value of isVisible to false (by passing a false to the useState().)

      const [isVisible, setVisible] = React.useState(false)

Leave a Reply