Emotional and Functional UI Animations in React
December 02, 2022 • 2 min read
Animation has the power to transform user experience, significantly influencing how users perceive a product. From providing visual feedback and indicating status changes to adding a playful touch, animations offer a plethora of possibilities to enhance interactivity and engagement.
During my talk at React day Berlin on December 2, 2022, I showcased several demos to demonstrate how both functional and emotional UI animations can make a React application not only more intuitive but also more memorable. These animations bring interfaces to life, making interactions smoother and more enjoyable for users.
A simple hover button animation
This is a functional micro-animation that is triggered by a user interaction. When a user hovers a button, the button should animate to indicate that the button is clickable.
import * as React from "react"; import { Box, Button } from "@chakra-ui/react"; import { css } from "@emotion/react"; export default function Example() { return ( <Box> <Button colorScheme='purple' size='lg' css={css` // transition: <property> <duration> <timing-function> <delay> transition: transform 250ms ease-in-out 0s, background-color 250ms ease-in-out 0s; &:hover { transform: translateY(-1px); } `}> I'm a Button </Button> </Box> ); }
A button that shows a line on click
This is a functional animation that is triggered by a user interaction. When a user hovers a button, the button should animate to indicate that the button is clickable.
import { useState } from "react"; import { Flex, Text } from "@chakra-ui/react"; import ButtonLine from "./ButtonLine"; export default function BellRinging() { const [activeItem, setActiveItem] = useState("about"); return ( <Flex direction="row" background="#111b27" justifyContent="space-between" alignItems="center" padding="1em" color="gray.100" > <Text fontSize={["2xl", "2xl", "md"]} fontWeight="semibold"> <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="#d6d8e7" > <circle cx="50" cy="50" r="50" /> </svg> </Text> <Flex alignItems="center" flexDirection="row"> <ButtonLine isActive={activeItem === "about"} onClick={() => setActiveItem("about")} > About </ButtonLine> <ButtonLine isActive={activeItem === "speaking"} onClick={() => setActiveItem("speaking")} > Speaking </ButtonLine> <ButtonLine isActive={activeItem === "blog"} onClick={() => setActiveItem("blog")} > Blog </ButtonLine> </Flex> </Flex> ); }
A bell ringing in a header
This is a functional animation that should be triggered by a state change in the application. It is used to provide feedback to the user about the state of the application. For example, when a user receives a notification, the bell should animate to indicate that the user has a new notification.
For this example we're checking if users have the reduce motion setting enabled in their operating system. If they do, we're not animating the bell.
import { useState } from "react"; import { BellIcon } from "@chakra-ui/icons"; import { Flex, Box, Button } from "@chakra-ui/react"; import { motion, useReducedMotion } from "framer-motion"; export default function BellRinging() { const [isRinging, setIsRinging] = useState(false); // A hook that returns true if the current device has Reduced Motion setting enabled. const shouldReduceMotion = useReducedMotion(); const rotations = shouldReduceMotion ? 0 : [ 0, 30, -28, 0, 34, -32, 30, -28, 26, -24, 22, -20, 18, -16, 14, -12, 10, -8, 6, -4, 2, -1, 0 ]; const rotationVariants = { inactive: { rotate: 0 }, active: { rotate: rotations } }; return ( <> <Flex direction="row" background="#111b27" justifyContent="space-between" alignItems="center" padding="1em" color="gray.100" > <span>Logo</span> <motion.span initial="inactive" animate={isRinging ? "active" : "inactive"} variants={rotationVariants} transition={{ duration: 2 }} onAnimationComplete={() => { setIsRinging(false); }} > <BellIcon width={6} height={6} color="gray.200" /> </motion.span> </Flex> <Box paddingTop="4"> <Button size="xs" onClick={() => setIsRinging(true)}> Animate </Button> </Box> </> ); }
A like button that animates on click
This is an emotional animation. It could be a simple like button but we're using an cute heart that animates on click.
import { useState } from "react"; import LoveButton from "./LoveButton"; import { Button, Flex, Box } from "@chakra-ui/react"; export default function Demo() { const [userLikes, setUserLikes] = useState(0); const [totalLikes, setTotalLikes] = useState(112); const onClickButton = () => { setUserLikes(userLikes + 1); setTotalLikes(totalLikes + 1); }; const onReset = () => { setUserLikes(0); }; return ( <Flex direction="column" justifyContent="center" alignItems="center"> <LoveButton onClickButton={onClickButton} userLikes={userLikes} totalLikes={totalLikes} /> <Box paddingTop="4"> <Button size="xs" onClick={onReset}> Reset user likes </Button> </Box> </Flex> ); }
A search bar with a planet that animates
This is an emotional animation. It was originally used on isthereuber.in.
import { useState } from "react"; import { Input, VStack, HStack, IconButton } from "@chakra-ui/react"; import { SearchIcon } from "@chakra-ui/icons"; import Planet from "./Planet"; export default function Demo() { const [value, setValue] = useState(""); const [isSearching, setSearching] = useState(false); const [isSearchComplete, setIsSearchComplete] = useState(false); const handleChange = (e: any) => { setValue(e.target.value); if (e.target.value) { setSearching(true); } else { setSearching(false); } }; const onClick = () => { setIsSearchComplete(true); }; const onAnimationComplete = () => { setIsSearchComplete(false); }; return ( <VStack> <Planet isSearching={isSearching} isSearchComplete={isSearchComplete} onAnimationComplete={onAnimationComplete} /> <HStack> <Input type="search" name="search" value={value} onChange={handleChange} /> <IconButton aria-label="Search" icon={<SearchIcon />} onClick={onClick} /> </HStack> </VStack> ); }