davidhu2000 / react-spinners

A collection of loading spinner components for react
https://www.davidhu.io/react-spinners
MIT License
3k stars 260 forks source link

Spinner isn't not Spinning in Nextjs 14 (Server Side) #576

Open mahsanr44 opened 7 months ago

mahsanr44 commented 7 months ago

I'm adding react-spinners in my Next.JS app but isn't spinning. Here's my code:

import { ClimbingBoxLoader} from "react-spinners";
export default function Home() {
  return (
    <div>
<ClimbingBoxLoader 
  color="#36d7b7"
  loading
  speedMultiplier={2}
/>
      <MyComponent />
    </div>
  );
}
mahsanr44 commented 7 months ago

I have tested by using use client and it's working fine on the Client Side but I want it to be useful on the Server Side too.

davidhu2000 commented 7 months ago

i'll need to spent some time looking into how to handle the new server component paradigm. I think it's probably not working because the animation are created outside of the component

https://github.com/davidhu2000/react-spinners/blob/main/src/ClimbingBoxLoader.tsx#L7-L21

so maybe that code isn't executed after the code is sent to the client

mahsanr44 commented 7 months ago

Yeah, you're right, this could be the one of reasons. Take your time and fix it. Thanks

milindgoel15 commented 6 months ago

client-side animation stuff cannot be rendered on the server. They need to be marked as client component to work. Its not something that's fixable imo.

JDuqn commented 3 months ago

For now I just force all my loaders to be client components with a fallback for server side :

"use client"

import React, { useEffect, useState } from "react"
import { ClipLoader } from "react-spinners"

export const Loader = ({ size }: { size: number }) => {
  const [isClient, setIsClient] = useState(false)

  useEffect(() => {
    setIsClient(true)
  }, [])

  return (
    <React.Fragment>
      {isClient ? (
        <ClipLoader ... />
      ) : (
        <ServerSideSpinner ... />
      )}
    </React.Fragment>
  )
}

ServerSideSpinner is a regular spinner animated with tailwind.

Not sure this is a good approach, but it might help someone.

milindgoel15 commented 3 months ago

Don't do this, make a separate file, add "use client" tag on top, import the spinner and immediately export it as const or default (,ur choice) and import this spinner in your pages.

davidhu2000 commented 3 months ago

i haven't had time to work on this recently, but one potential approach would be add use client on top of each loader, and add a setState call for each animation. This ensures it will run on the client.

const [animation] = useState(createAnimation(...))

a quick test locally does seem to work, just need to update it all. Maybe hack on it in a coming weekend.

i thought maybe using next dynamic could do it, but the create Aniamtion still doesn't run on the client

const Loader = dynamic(() => import("react-spinners/ClimbingBoxLoader"), { ssr: false });
JDuqn commented 3 months ago
const [animation] = useState(createAnimation(...))

This will execute createAnimation on every render uselessly, I would advise using a callback

const [animation] = useState(() => createAnimation(...))