Logo

dev-resources.site

for different kinds of informations.

How to improve the Frontend part of the project using one button as an example :))))

Published at
1/14/2025
Categories
frontend
gsap
react
ts
Author
sadbatya
Categories
4 categories in total
frontend
open
gsap
open
react
open
ts
open
Author
8 person written this
sadbatya
open
How to improve the Frontend part of the project using one button as an example :))))

Hello everyone! I am a junior frontend developer. And I would like to tell you how sometimes using libraries in a project is an excess that should be avoided.

Let's start in order. The project is written in NextJs, TS, TailwindCSS. And there is an animated button on the site that appears beautifully when scrolling and opens a modal when clicked.

Image description

All the logic is written in Gsap. And it would seem that everything is fine, the library does the job for us, draws animations, and we calmly drink tea and send PR to GitHub.
BUT. The problem was that sometimes the animation froze, twitched, and on weak PCs it refused to work at all.
In the end, I decided to take on the animation button and see what's inside it :)
And, oh, MIRACLE! If you look at the component, it seems very scary to me. What do you think?

`export const AnimatedButtonLinkWitmUtm: React.FC = ({
isDark = false,
btnColor = '#0A85D1',
btnShadow = '0 0 0 0px rgb(157,52,218)',
maxWidth = 315,
}) => {
const buttonRef = useRef();
const tl = gsap.timeline();

useEffect(() => {
if (buttonRef.current) {
const trigger = ScrollTrigger.create({
trigger: buttonRef.current,
start: top bottom-=400px,
onEnter: () => {
tl.to(buttonRef.current, { opacity: 1, duration: 0.3 })
.to(buttonRef.current, {
duration: 0.3,
boxShadow: btnShadow,
ease: 'circ.in',
})
.to(buttonRef.current, {
duration: 0.3,
boxShadow: btnShadow,
ease: 'circ.out',
})
.to(buttonRef.current, {
maxWidth: ${maxWidth}px,
width: '100%',
paddingLeft: 24,
duration: 1,
})
.to(buttonRef.current.children[1], { opacity: 1, duration: 2 }, '<0.4')
.to(buttonRef.current.children[0], { opacity: 1, duration: 2 }, '<0.5');
},
onLeaveBack: () => {
tl.to(buttonRef.current, { opacity: 0 })
.to(buttonRef.current.children[1], { opacity: 0 })
.to(buttonRef.current, { width: '55px', paddingLeft: 10 })
.to(buttonRef.current.children[0], { opacity: 0, delay: 0, duration: 0 });
},
});

  return () => {
    trigger.kill();
  };
}
Enter fullscreen mode Exit fullscreen mode

}, [btnShadow, maxWidth, tl]);

return (
<>

    ref={buttonRef}<br>
    className={cn(<br>
      'relative flex h-[56px] w-[56px] items-center justify-between gap-[16px] rounded-[10px] px-[10px] py-[8px] opacity-0 shadow-[inset_0px_1px_0px_0px_rgba(0,0,0,0.11)] backdrop-blur-[3.5px]',<br>
      {<br>
        'bg-[#D8D8D8]/30': !isDark,<br>
        'bg-[#424245]/70': isDark,<br>
      }<br>
    )}<br>
  &gt;<br>

      href="https://a6b9d8dc-b142-4b92-b1d0-dfbfd2230471.selstorage.ru/assets/edu-program.pdf"<br>
      target="_blank"<br>
      className={cn(<br>
        'm-0 text-[17px] font-medium leading-[27.2px] opacity-0 after:absolute after:inset-0',<br>
        {<br>
          'text-black': !isDark,<br>
          'text-[#F5F5F7]': isDark,<br>
        }<br>
      )}<br>
    &gt;<br>
      Подробнее о программе<br>
    <br>

      className="h-[40px] w-[40px] min-w-[40px] rounded-[10px] p-[8px] opacity-0"<br>
      style={{ backgroundColor: btnColor }}<br>
    &gt;<br>
      <br>
    <br>
  <br>
&lt;/&gt;<br>
Enter fullscreen mode Exit fullscreen mode

);

};

`

We have logic on GSAP and a trigger at what point of scrolling it should appear.

If you look at the animation, it is based on the size and opacity.

Do we need GSAP for such a simple animation??? NO. And let me try to explain why.
1 - Library weight. For the sake of the simplest animation, you pull a library weighing 4 MB into the project.
2 - This animation can be done in pure CSS

Image description

We will write the animation, but for the trigger, so that everything works, we only need Observer. All this can be implemented using hooks, but for such a situation there is a library much lighter and easier to use and under the hood it has React hooks.

Image description
As you can see, the package is much lighter and more popular in terms of downloads, as it covers simple needs for triggers and animations:)

And what did I end up with?
`export const NewAnimatedButtonWithLinkUtm = ({
isDark = false,
btnColor = '#0A85D1',
}: IAnimatedButtonProps) => {
const { ref, inView } = useInView();

return (

  ref={ref}<br>
  className={twMerge(<br>
    'sticky bottom-8 top-[80vh] mx-auto mt-8 flex h-[56px] cursor-pointer items-center justify-between gap-[16px] rounded-[10px] px-4 py-[8px] shadow-md backdrop-blur-[3.5px] transition-all',<br>
    isDark ? 'bg-[#424245]/70' : 'bg-[#D8D8D8]/30',<br>
    inView ? `opacity-1 w-[315px] delay-500 duration-1000` : 'w-[65px] opacity-0'<br>
  )}<br>
&gt;<br>

    href={linkHref}<br>
    className={twJoin(<br>
      'm-0 text-[17px] font-medium transition-all after:absolute after:inset-0',<br>
      isDark ? 'bg-[#424245]/70 text-[#F5F5F7]' : 'bg-transparent text-black',<br>
      inView ? 'opacity-1 delay-1000 duration-1000' : 'opacity-0'<br>
    )}<br>
  &gt;<br>
    Начать учиться бесплатно<br>
  <br>

    className={twJoin(<br>
      `absolute right-3 h-[40px] w-[40px] min-w-[40px] animate-pulse rounded-[10px] p-2 transition-all bg-[${btnColor}]`,<br>
      inView ? 'opacity-1' : 'opacity-0'<br>
    )}<br>
  &gt;<br>
    <br>
  <br>
<br>
Enter fullscreen mode Exit fullscreen mode

);

};`

As you can see, instead of 85 lines of code and complex logic, I have 36 lines of code and the styles are applied via a logical value. I think it will be much easier to refactor such code)

BUT. That's not all. The problem was that it appeared immediately as the section entered the user's viewport, and we need the user to scroll 30% of the section and only after that the animation would work. And here, honestly, I slowed down a little and started googling, although after a couple of hours the logic turned out to be very simple.

I created a container in which the button is located and through children it receives the section along which the user will scroll and this is what happened))

`export const SectionNewAnimatedButtonWithLinkUtm = ({
isDark = false,
btnClassName,
children,
className = 'relative mx-auto mb-40 max-w-[1024px] px-5 lg:px-0',
text = 'Начать учиться бесплатно',
}: IAnimatedButtonProps) => {
const { ref, inView } = useInView({
threshold: 0.2,
});

return (

{children}
className={twMerge(
'sticky bottom-8 top-[80vh] mx-auto mt-8 flex h-[56px] cursor-pointer items-center justify-between gap-[16px] rounded-[10px] px-4 py-[8px] shadow-md backdrop-blur-[3.5px] transition-all',
isDark ? 'bg-[#424245]/70' : 'bg-[#D8D8D8]/30',
inView
? `opacity-1 w-[315px] delay-500 duration-1000`
: 'w-[65px] opacity-0 duration-1000'
)}
>
href={linkHref}
className={twJoin(
'm-0 text-[17px] font-medium transition-all after:absolute after:inset-0',
isDark ? 'bg-[#424245]/70 text-[#F5F5F7]' : 'bg-transparent text-black',
inView ? 'opacity-1 delay-1000 duration-200' : 'opacity-0'
)}
>
{text}

className={twMerge(
`absolute right-3 h-[40px] w-[40px] min-w-[40px] animate-pulse rounded-[10px] bg-[#0A85D1] p-2 transition-all`,
`${btnClassName}`,
inView ? 'opacity-1' : 'opacity-0'
)}
>




);
};`

10 lines of code have been added, but that's okay. The code is still clear)

It seems that's all, you can open the PR and wait for a response from the Team Lead and merge into the main branch.

NOOOO)))))) We can still pump up our component and follow one of the OOP principles - Open/Closed and hide all the logic under the hood so that the frontend developer in the team uses the component and does not have to get into the guts.

And then I decided to link the container and the button component through a regular context in React)
First of all, we create a context and a container and pass the main value for the animation

`export const AnimatedButtonContext = createContext(null);

export const SectionAnimatedButton = ({ children }) => {
const { ref, inView } = useInView({
threshold: 0.2,
});

return (


{children}



);
};`

Then in the button itself we use the useContext hook and bind to the context

`export const AnimatedBtn = ({
isDark = false,
btnClassName,
text = 'Начать учиться бесплатно',
link = '',
}: IAnimatedButtonProps) => {
const { inView } = useContext(AnimatedButtonContext);

return (

  className={twMerge(<br>
    'sticky bottom-8 top-[80vh] mx-auto mt-8 flex h-[56px] cursor-pointer items-center justify-between gap-[16px] rounded-[10px] px-4 py-[8px] shadow-md backdrop-blur-[3.5px] transition-all',<br>
    isDark ? 'bg-[#424245]/70' : 'bg-[#D8D8D8]/30',<br>
    inView ? `opacity-1 w-[315px] delay-500 duration-1000` : 'w-[65px] opacity-0 duration-1000'<br>
  )}<br>
&gt;<br>

    href={link}<br>
    className={twJoin(<br>
      'm-0 text-[17px] font-medium transition-all after:absolute after:inset-0',<br>
      isDark ? 'bg-[#424245]/70 text-[#F5F5F7]' : 'bg-transparent text-black',<br>
      inView ? 'opacity-1 delay-1000 duration-200' : 'opacity-0'<br>
    )}<br>
  &gt;<br>
    {text}<br>
  <br>

    className={twMerge(<br>
      `absolute right-3 h-[40px] w-[40px] min-w-[40px] animate-pulse rounded-[10px] bg-[#0A85D1] p-2 transition-all`,<br>
      `${btnClassName}`,<br>
      inView ? 'opacity-1' : 'opacity-0'<br>
    )}<br>
  &gt;<br>
    <br>
  <br>
<br>
Enter fullscreen mode Exit fullscreen mode

);

};`

Now the component is READY)

What we have achieved as a result of this work:

1 - Removed a heavy library
2 - I will make the button logic simpler and therefore it will be much easier to refactor
3 - Hid the logic and created a container in which we wrap our content and everything works))
4 - Animation began to work smoothly and without freezing, including on weak PCs

The article turned out to be a little chaotic, but I wanted to say that using libraries is not always good and sometimes it is useful to get into that very DATABASE and look at a simple solution)

Thanks to everyone who read it to the end! I was glad to tell you about the part-time project I work on and therefore I want to say that I am open to offers to offers and if you need an active and ambitious developer, I will be glad to talk to you and flex your code :)
https://t.me/sadbatya

And with you was Vladimir! Have a nice day, evening and night :)

react Article's
30 articles in total
React is a JavaScript library for building user interfaces, enabling developers to create reusable components and dynamic web applications.
Favicon
Redux Middleware সম্পর্কে বিস্তারিত আলোচনা
Favicon
POST ABOUT AI'S INCREASING INFLUENCE IN CODING
Favicon
[Boost]
Favicon
🌟 A New Adventure Begins! 🛵🍕
Favicon
From Heist Strategy to React State: How data flows between components
Favicon
Understanding React's useState with Callback Functions: A Deep Dive
Favicon
From Chaos to Clarity: Formatting React Code for a Clean and Readable Codebase
Favicon
Creating a react game on AWS
Favicon
Refactoring React: Taming Chaos, One Component at a Time
Favicon
The Magic of useCallback ✨
Favicon
Show a loading screen when changing pages in Next.js App router
Favicon
How to improve the Frontend part of the project using one button as an example :))))
Favicon
Open-Source TailwindCSS React Color Picker - Zero Dependencies! Perfect for Next.js Projects!
Favicon
Introducing EAS Hosting: Simplified deployment for modern React apps
Favicon
Understanding React's useEffect and Event Listeners: A Deep Dive
Favicon
Recreating the Interswitch Homepage with React and TailwindCSS.
Favicon
How to receive data in form from another component
Favicon
Unlocking the Secrets of React Context: Power, Pitfalls, and Performance
Favicon
"Starting My React Journey"
Favicon
Open-Source React Icon Picker: Lightweight, Customizable, and Built with ShadCN, TailwindCSS. Perfect for Next.js Projects!
Favicon
Dynamically Render Components Based on Configuration
Favicon
Conquer Breakpoints with React's useBreakpoints Hook
Favicon
Using React as Static Files in a Django Application: Step-by-Step Guide
Favicon
Don't copy/paste code you don't understand
Favicon
All-APIs.com: The Ultimate Free REST API Platform for Developers
Favicon
Form-based Dataverse Web Resources with React, Typescript and FluentUI - Part 2
Favicon
Level Up React : Deep Dive into React Elements
Favicon
Building Production-Grade Web Applications with Supabase – Part 1
Favicon
Transform Your Web Development Workflow with These JavaScript Giants
Favicon
Building High-Performance React Native Apps[Tips for Developers]

Featured ones: