{"id":2727,"date":"2026-06-06T03:36:09","date_gmt":"2026-06-06T03:36:09","guid":{"rendered":"https:\/\/tucumandevelopers.com\/index.php\/2026\/06\/06\/my-first-react-project-part-3-reusable-components-framer-motion-animation-and-key-lessons-learned\/"},"modified":"2026-06-06T03:36:09","modified_gmt":"2026-06-06T03:36:09","slug":"my-first-react-project-part-3-reusable-components-framer-motion-animation-and-key-lessons-learned","status":"publish","type":"post","link":"https:\/\/tucumandevelopers.com\/index.php\/2026\/06\/06\/my-first-react-project-part-3-reusable-components-framer-motion-animation-and-key-lessons-learned\/","title":{"rendered":"My First React Project (Part 3): Reusable Components, Framer Motion Animation, and Key Lessons Learned"},"content":{"rendered":"<div>\n<div><\/div>\n<p>Then I reused it inside my LandingPage component: <\/p>\n<div>\n<pre><code><span>function<\/span> <span>LandingPage<\/span><span>()<\/span> <span>{<\/span> <span>return<\/span><span>(<\/span> <span>&lt;&gt;<\/span> <span>{<\/span><span>\/* other LandingPage JSX *\/<\/span><span>}<\/span> <span>&lt;<\/span><span>section<\/span> <span>id<\/span><span>=<\/span><span>\"features\"<\/span><span>&gt;<\/span> <span>&lt;<\/span><span>Section<\/span> <span>backgroundColor<\/span><span>=<\/span><span>\"section--gray-100\"<\/span> <span>title<\/span><span>=<\/span><span>\"Why choose Digitalbank?\"<\/span> <span>description<\/span><span>=<\/span><span>\"We leverage Open Banking to turn your bank account into your financial hub. Control your finances like never before.\"<\/span> <span>&gt;<\/span> <span>&lt;<\/span><span>Features<\/span> <span>\/&gt;<\/span> <span>&lt;\/<\/span><span>Section<\/span><span>&gt;<\/span> <span>&lt;\/<\/span><span>section<\/span><span>&gt;<\/span> <span>&lt;<\/span><span>section<\/span> <span>id<\/span><span>=<\/span><span>\"articles\"<\/span><span>&gt;<\/span> <span>&lt;<\/span><span>Section<\/span> <span>backgroundColor<\/span><span>=<\/span><span>\"section--gray-50\"<\/span> <span>title<\/span><span>=<\/span><span>\"Latest Articles\"<\/span> <span>&gt;<\/span> <span>&lt;<\/span><span>Articles<\/span> <span>\/&gt;<\/span> <span>&lt;\/<\/span><span>Section<\/span><span>&gt;<\/span> <span>&lt;\/<\/span><span>section<\/span><span>&gt;<\/span> <span>{<\/span><span>\/* other LandingPage JSX *\/<\/span><span>}<\/span> <span>&lt;\/&gt;<\/span> <span>);<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>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.<\/p>\n<p>That was one of the biggest &#8220;React mindset&#8221; moments during the project.<\/p>\n<h2> <a name=\"adding-animations-with-css-react-hooks\" href=\"#adding-animations-with-css-react-hooks\"> <\/a> Adding Animations with CSS + React Hooks <\/h2>\n<p>For the Hero Section, I wanted a fade-in + slide-up animation when the mockup image loads.<\/p>\n<p>In plain HTML\/CSS, I&#8217;d rely on CSS animations triggered by page load. But in React, I used the useState hook with the onLoad event: <\/p>\n<div>\n<pre><code><span>function<\/span> <span>HeroSection<\/span><span>()<\/span> <span>{<\/span> <span>const<\/span> <span>[<\/span><span>imageLoaded<\/span><span>,<\/span> <span>setImageLoaded<\/span><span>]<\/span> <span>=<\/span> <span>useState<\/span><span>(<\/span><span>false<\/span><span>);<\/span> <span>return<\/span><span>(<\/span> <span>&lt;<\/span><span>section<\/span> <span>className<\/span><span>=<\/span><span>{<\/span><span>`hero <\/span><span>${<\/span><span>imageLoaded<\/span> <span>?<\/span> <span>\"<\/span><span>loaded<\/span><span>\"<\/span> <span>:<\/span> <span>\"\"<\/span><span>}<\/span><span>`<\/span><span>}<\/span> <span>aria-labelledby<\/span><span>=<\/span><span>\"hero-title\"<\/span> <span>&gt;<\/span> <span>{<\/span><span>\/* other hero JSX *\/<\/span><span>}<\/span> <span>&lt;<\/span><span>img<\/span> <span>src<\/span><span>=<\/span><span>{<\/span><span>mockup<\/span><span>}<\/span> <span>alt<\/span><span>=<\/span><span>\"Mockup of a banking app on mobile phones\"<\/span> <span>className<\/span><span>=<\/span><span>\"hero__mockup\"<\/span> <span>onLoad<\/span><span>=<\/span><span>{<\/span><span>()<\/span> <span>=&gt;<\/span> <span>setImageLoaded<\/span><span>(<\/span><span>true<\/span><span>)<\/span><span>}<\/span> <span>\/&gt;<\/span> <span>{<\/span><span>\/* other hero JSX *\/<\/span><span>}<\/span> <span>&lt;\/<\/span><span>section<\/span><span>&gt;<\/span> <span>);<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Here&#8217;s how it works:<\/p>\n<ul>\n<li>imageLoaded starts as false<\/li>\n<li>Once the image finishes loading, setImageLoaded(true) runs<\/li>\n<li>React adds the loaded class<\/li>\n<li>The CSS animation then triggers<\/li>\n<\/ul>\n<p>This ensures the animation plays only after the image is fully ready, creating a smoother experience.<\/p>\n<h2> <a name=\"staggered-animations-with-framer-motion\" href=\"#staggered-animations-with-framer-motion\"> <\/a> Staggered Animations with Framer Motion <\/h2>\n<p>The last animation I added was a staggered reveal for the article cards.<\/p>\n<p>Instead of writing complex JavaScript with the Intersection Observer API, I used Framer Motion, which is declarative and integrates beautifully with React.<\/p>\n<p>I defined variants for the parent container and child cards: <\/p>\n<div>\n<pre><code><span>\/\/ Section component<\/span> <span>const<\/span> <span>containerVariants<\/span> <span>=<\/span> <span>{<\/span> <span>hidden<\/span><span>:<\/span> <span>{},<\/span> <span>visible<\/span><span>:<\/span> <span>{<\/span> <span>transition<\/span><span>:<\/span> <span>shouldReduceMotion<\/span> <span>?<\/span> <span>{}<\/span> <span>:<\/span> <span>{<\/span> <span>staggerChildren<\/span><span>:<\/span> <span>0.12<\/span> <span>}<\/span> <span>}<\/span> <span>};<\/span> <span>\/\/ Articles component<\/span> <span>const<\/span> <span>cardVariants<\/span> <span>=<\/span> <span>{<\/span> <span>hidden<\/span><span>:<\/span> <span>{<\/span> <span>opacity<\/span><span>:<\/span> <span>shouldReduceMotion<\/span> <span>?<\/span> <span>1<\/span> <span>:<\/span> <span>0<\/span><span>,<\/span> <span>y<\/span><span>:<\/span> <span>shouldReduceMotion<\/span> <span>?<\/span> <span>0<\/span><span>:<\/span> <span>30<\/span> <span>},<\/span> <span>visible<\/span><span>:<\/span> <span>{<\/span> <span>opacity<\/span><span>:<\/span> <span>1<\/span><span>,<\/span> <span>y<\/span><span>:<\/span> <span>0<\/span><span>,<\/span> <span>transition<\/span><span>:<\/span> <span>shouldReduceMotion<\/span> <span>?<\/span> <span>{<\/span> <span>duration<\/span><span>:<\/span> <span>0<\/span> <span>}<\/span> <span>:<\/span> <span>{<\/span> <span>duration<\/span><span>:<\/span> <span>0.5<\/span><span>,<\/span> <span>ease<\/span><span>:<\/span> <span>\"<\/span><span>easeOut<\/span><span>\"<\/span> <span>}<\/span> <span>}<\/span> <span>};<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Then applied them: <\/p>\n<div>\n<pre><code><span>\/\/ Section component<\/span> <span>&lt;<\/span><span>motion<\/span><span>.<\/span><span>div<\/span> <span>variants<\/span><span>=<\/span><span>{<\/span><span>containerVariants<\/span><span>}<\/span> <span>initial<\/span><span>=<\/span><span>{<\/span><span>shouldReduceMotion<\/span> <span>?<\/span> <span>false<\/span> <span>:<\/span> <span>\"<\/span><span>hidden<\/span><span>\"<\/span><span>}<\/span> <span>whileInView<\/span><span>=<\/span><span>{<\/span><span>shouldReduceMotion<\/span> <span>?<\/span> <span>undefined<\/span> <span>:<\/span> <span>\"<\/span><span>visible<\/span><span>\"<\/span><span>}<\/span> <span>viewport<\/span><span>=<\/span><span>{<\/span><span>{<\/span> <span>once<\/span><span>:<\/span> <span>true<\/span><span>,<\/span> <span>amount<\/span><span>:<\/span> <span>0.2<\/span> <span>}<\/span><span>}<\/span> <span>className<\/span><span>=<\/span><span>\"section__grid\"<\/span> <span>&gt;<\/span> <span>{<\/span><span>children<\/span><span>}<\/span> <span>&lt;\/<\/span><span>motion<\/span><span>.<\/span><span>div<\/span><span>&gt;<\/span> <span>\/\/ Articles component<\/span> <span>&lt;<\/span><span>motion<\/span><span>.<\/span><span>article<\/span> <span>variants<\/span><span>=<\/span><span>{<\/span><span>cardVariants<\/span><span>}<\/span> <span>className<\/span><span>=<\/span><span>\"article__card\"<\/span> <span>key<\/span><span>=<\/span><span>{<\/span><span>article<\/span><span>.<\/span><span>id<\/span><span>}<\/span> <span>&gt;<\/span> <span>{<\/span><span>\/* article content *\/<\/span><span>}<\/span> <span>&lt;\/<\/span><span>motion<\/span><span>.<\/span><span>article<\/span><span>&gt;<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>The result is a smooth, staggered animation in which each card slides upward one after another.<\/p>\n<p>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.<\/p>\n<h2> <a name=\"key-lessons-learned\" href=\"#key-lessons-learned\"> <\/a> Key Lessons Learned <\/h2>\n<h3> <a name=\"1-component-architecture\" href=\"#1-component-architecture\"> <\/a> 1. Component Architecture <\/h3>\n<p>Breaking the UI into reusable pieces made the project much easier to manage.<\/p>\n<p>Each component owns its own markup and logic, while the LandingPage component simply arranges sections together.<\/p>\n<p>That separation made the codebase feel more organized and scalable.<\/p>\n<h3> <a name=\"2-react-hooks\" href=\"#2-react-hooks\"> <\/a> 2. React Hooks <\/h3>\n<p>Hooks became essential throughout this project.<\/p>\n<p>I used:<\/p>\n<ul>\n<li>useState for menu toggles and image load tracking<\/li>\n<li>useEffect for disabling page scroll when the mobile menu is open<\/li>\n<\/ul>\n<p>Before this project, hooks felt abstract while reading documentation. Building something real made them finally &#8220;click&#8221; for me.<\/p>\n<h3> <a name=\"3-props-and-state-management\" href=\"#3-props-and-state-management\"> <\/a> 3. Props and State Management <\/h3>\n<p>Passing props down and lifting state up are core React patterns that I practiced repeatedly here.<\/p>\n<p>The more I used them, the more I understood how React components communicate and stay flexible.<\/p>\n<h3> <a name=\"4-css-was-harder-than-react\" href=\"#4-css-was-harder-than-react\"> <\/a> 4. CSS Was Harder Than React <\/h3>\n<p>Ironically, CSS ended up being the hardest part of the project.<\/p>\n<p>I spent far more time dealing with:<\/p>\n<ul>\n<li>layouts<\/li>\n<li>spacing<\/li>\n<li>positioning<\/li>\n<li>responsiveness<\/li>\n<li>overflow issues<\/li>\n<\/ul>\n<p>than writing actual React logic.<\/p>\n<p>Using functions like clamp() across breakpoints helped improve my responsive design skills a lot.<\/p>\n<h3> <a name=\"5-accessibility-matters\" href=\"#5-accessibility-matters\"> <\/a> 5. Accessibility Matters <\/h3>\n<p>I also tried to make the project more accessible by:<\/p>\n<ul>\n<li>adding aria-labelledby<\/li>\n<li>respecting prefers-reduced-motion<\/li>\n<li>using Framer Motion&#8217;s useReducedMotion hook<\/li>\n<\/ul>\n<p>This reminded me that accessibility shouldn&#8217;t be treated as an afterthought.<\/p>\n<h2> <a name=\"final-thoughts\" href=\"#final-thoughts\"> <\/a> Final Thoughts <\/h2>\n<p>This project tested both my CSS fundamentals and React basics.<\/p>\n<p>Even though it wasn&#8217;t the most React-heavy application, it gave me hands-on experience with:<\/p>\n<ul>\n<li>component architecture<\/li>\n<li>hooks<\/li>\n<li>reusable layouts<\/li>\n<li>Framer Motion<\/li>\n<li>accessibility considerations<\/li>\n<\/ul>\n<p>More importantly, it taught me persistence.<\/p>\n<p>I got stuck multiple times. I rewrote code, researched solutions, broke layouts, and made mistakes, but I kept going until everything finally came together.<\/p>\n<p>And honestly, that might be the biggest lesson from this entire project.<\/p>\n<p>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.<\/p>\n<p>On to the next project!<\/p>\n<\/p><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Fuente: <a href=\"https:\/\/dev.to\/ayra_austine\/my-first-react-project-part-3-reusable-components-framer-motion-animation-and-key-lessons-d95\">Art\u00edculo original<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Then I reused it inside my LandingPage component: function LandingPage() { return( &lt;&gt; {\/* other LandingPage JSX *\/} &lt;section id=&#8221;features&#8221;&gt; &lt;Section backgroundColor=&#8221;section&#8211;gray-100&#8243; title=&#8221;Why choose Digitalbank?&#8221; description=&#8221;We leverage Open Banking to turn your bank account into your financial hub. Control your finances like never before.&#8221; &gt; &lt;Features \/&gt; &lt;\/Section&gt; &lt;\/section&gt; &lt;section id=&#8221;articles&#8221;&gt; &lt;Section backgroundColor=&#8221;section&#8211;gray-50&#8243; title=&#8221;Latest Articles&#8221; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2726,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[41],"tags":[],"class_list":["post-2727","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devto"],"jetpack_publicize_connections":[],"_links":{"self":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/2727","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/comments?post=2727"}],"version-history":[{"count":0,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/2727\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media\/2726"}],"wp:attachment":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media?parent=2727"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/categories?post=2727"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/tags?post=2727"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}