DEV Community

Cover image for My First React Project (Part 3): Reusable Components, Framer Motion Animation, and Key Lessons Learned
Ayra Austine Baet
Ayra Austine Baet

Posted on

My First React Project (Part 3): Reusable Components, Framer Motion Animation, and Key Lessons Learned

This is the third and final part of my first React project for the Frontend Mentor's Digital Bank Landing Page Challenge. I'm excited to say that I finally finished it.

Live Demo: https://bank-landing-page-react-gmtz.vercel.app/
Github Repo: https://github.com/ayra-baet/bank-landing-page-react

Learning Component Reusability Beyond Small Elements

At first, I thought this final part would mostly involve finishing the Articles and Footer. But while building, I realized something more important:

React's reusability isn't limited to small UI elements like buttons or cards; entire sections can be reusable too.

Earlier in this project, I reused a single Button component across the header, hero, and footer. This time, I noticed that the Features and Articles sections shared almost the same structure:

  • both had an h2 heading
  • both used a grid layout
  • both wrapped child components

The only real difference was that the Features section included a description paragraph.

That immediately felt like a perfect use case for a reusable component with conditional rendering.

So I created a reusable Section component:

function Section({ backgroundColor, title, description, children }) {
    return(
        <section 
            className={backgroundColor}
            aria-labelledby={`${title}-heading`}
        >
            <div className="container section__container">
                <div className="section__header">
                    <h2 id={`${title}-heading`}>{title}</h2>
                    {description && <p>{description}</p>}
                </div>

                <div className="section__grid">
                    {children}
                </div>
            </div>
        </section>
    );
}
Enter fullscreen mode Exit fullscreen mode

Then I reused it inside my LandingPage component:

function LandingPage() {
    return(
        <>
            {/* other LandingPage JSX */}

                <section id="features">
                    <Section
                        backgroundColor="section--gray-100"
                        title="Why choose Digitalbank?"
                        description="We leverage Open Banking to turn your bank account into your financial hub. Control your finances like never before."
                >
                        <Features />
                    </Section>
                </section>

                <section id="articles">
                    <Section
                        backgroundColor="section--gray-50"
                        title="Latest Articles"
                    >
                        <Articles />
                    </Section>
                </section>

            {/* other LandingPage JSX */}
        </>
    );
}
Enter fullscreen mode Exit fullscreen mode

This made the code feel much cleaner and more scalable. If I ever add another section with the same structure, I can simply reuse the component instead of rewriting markup from scratch.

That was one of the biggest "React mindset" moments during the project.

Adding Animations with CSS + React Hooks

For the Hero Section, I wanted a fade-in + slide-up animation when the mockup image loads.

In plain HTML/CSS, I'd rely on CSS animations triggered by page load. But in React, I used the useState hook with the onLoad event:

function HeroSection() {
    const [imageLoaded, setImageLoaded] = useState(false);

    return(
        <section 
            className={`hero ${imageLoaded ? "loaded" : ""}`}
            aria-labelledby="hero-title"
        >
            {/* other hero JSX */}
            <img 
                src={mockup}
                alt="Mockup of a banking app on mobile phones"
                className="hero__mockup"
                onLoad={() => setImageLoaded(true)} 
            />

            {/* other hero JSX */}
        </section>
    );
}
Enter fullscreen mode Exit fullscreen mode

Here's how it works:

  • imageLoaded starts as false
  • Once the image finishes loading, setImageLoaded(true) runs
  • React adds the loaded class
  • The CSS animation then triggers

This ensures the animation plays only after the image is fully ready, creating a smoother experience.

Staggered Animations with Framer Motion

The last animation I added was a staggered reveal for the article cards.

Instead of writing complex JavaScript with the Intersection Observer API, I used Framer Motion, which is declarative and integrates beautifully with React.

I defined variants for the parent container and child cards:

// Section component
    const containerVariants = {
        hidden: {},
        visible: {
            transition: shouldReduceMotion
            ? {}
            : {
                staggerChildren: 0.12
            }
        }
    };

// Articles component
    const cardVariants = {
        hidden: {
            opacity: shouldReduceMotion ? 1 : 0,
            y: shouldReduceMotion ? 0: 30
        },
        visible: {
            opacity: 1,
            y: 0,
            transition: shouldReduceMotion
            ? { duration: 0 }
            : {
                duration: 0.5,
                ease: "easeOut"
            }
        }
    };
Enter fullscreen mode Exit fullscreen mode

Then applied them:

// Section component
    <motion.div
        variants={containerVariants}
        initial={shouldReduceMotion ? false : "hidden"}
        whileInView={shouldReduceMotion ? undefined : "visible"}
        viewport={{ once: true, amount: 0.2 }}
        className="section__grid"
    >
        {children}
    </motion.div>

// Articles component
    <motion.article
        variants={cardVariants}
        className="article__card" 
        key={article.id}
    >
        {/* article content */}
    </motion.article>
Enter fullscreen mode Exit fullscreen mode

The result is a smooth, staggered animation in which each card slides upward one after another.

What I liked most was how simple Framer Motion made everything. The whileInView prop automatically handles scroll-triggered animations without needing to manually wire up observers.

Key Lessons Learned

1. Component Architecture

Breaking the UI into reusable pieces made the project much easier to manage.

Each component owns its own markup and logic, while the LandingPage component simply arranges sections together.

That separation made the codebase feel more organized and scalable.

2. React Hooks

Hooks became essential throughout this project.

I used:

  • useState for menu toggles and image load tracking
  • useEffect for disabling page scroll when the mobile menu is open

Before this project, hooks felt abstract while reading documentation. Building something real made them finally "click" for me.

3. Props and State Management

Passing props down and lifting state up are core React patterns that I practiced repeatedly here.

The more I used them, the more I understood how React components communicate and stay flexible.

4. CSS Was Harder Than React

Ironically, CSS ended up being the hardest part of the project.

I spent far more time dealing with:

  • layouts
  • spacing
  • positioning
  • responsiveness
  • overflow issues

than writing actual React logic.

Using functions like clamp() across breakpoints helped improve my responsive design skills a lot.

5. Accessibility Matters

I also tried to make the project more accessible by:

  • adding aria-labelledby
  • respecting prefers-reduced-motion
  • using Framer Motion's useReducedMotion hook

This reminded me that accessibility shouldn't be treated as an afterthought.

Final Thoughts

This project tested both my CSS fundamentals and React basics.

Even though it wasn't the most React-heavy application, it gave me hands-on experience with:

  • component architecture
  • hooks
  • reusable layouts
  • Framer Motion
  • accessibility considerations

More importantly, it taught me persistence.

I got stuck multiple times. I rewrote code, researched solutions, broke layouts, and made mistakes, but I kept going until everything finally came together.

And honestly, that might be the biggest lesson from this entire project.

I now feel much more confident building future React applications, and I can already see how much more structured my approach has become compared to when I started.

On to the next project!

Top comments (1)

Collapse
 
merbayerp profile image
Mustafa ERBAY

What I find interesting is that your “React mindset” moment wasn’t really about React.

It was about abstraction.

First you reuse buttons.
Then you reuse cards.
Then you reuse sections.
Eventually you start seeing patterns, boundaries, and responsibilities everywhere.

That’s one of the biggest shifts in software engineering: moving from writing code to identifying reusable concepts.

The framework changes. That way of thinking tends to stay with you.