skip to Main Content

I have a client-side React component with an explicit use client declared in it:

"use client"

import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { useState, useTransition } from "react"
import { useSearchParams } from "next/navigation"
import { LoginSchema } from "@/schemas"
import login from "@/app/actions/login"

export default function LoginForm() {
  const currentURL = `/${window.location.href.split("/").slice(3).join("/")}`
  const searchParams = useSearchParams()
  const callbackUrl = searchParams.get("callback") || currentURL

  const [isPending, startTransition] = useTransition()

  const form = useForm<z.infer<typeof LoginSchema>>({
    resolver: zodResolver(LoginSchema),
    defaultValues: {
      email: "",
      password: "",
    },
  })

  const onSubmit = (values: z.infer<typeof LoginSchema>) => {
    startTransition(() => {
      login(values, callbackUrl)
        .then((data) => {
          if (data?.error) {
            form.reset()
            setError(data.error)
          }
          if (data?.success) {
            form.reset()
            setSuccess(data.success)
          }
          if (data?.twoFactor) {
            setShowTwoFactor(true)
          }
        })
        .catch(() => setError("Something went wrong"))
    })
  }

  return (
    <form className="space-y-6" onSubmit={form.handleSubmit(onSubmit)}>
      ...
    </form>
  );
}

I am getting the following error when trying to access the URL via window.location.href:

ReferenceError: window is not defined at LoginForm (./src/components/auth/LoginForm.tsx:40:28)

My understanding is that the window object is only available on the client so this should have worked. What am I doing wrong?

What I tried
I added a console.log() inside the function to print out the evaluated value of currentURL like so:

const currentURL = `/${window.location.href.split("/").slice(3).join("/")}`
console.log(currentURL)

And this worked just fine. So the component is clearly able to access the window object. But the server is still throwing the error mentioned above. In fact, I don’t understand why the server is even giving the error considering the component is being rendered on the client!

2

Answers


  1. Chosen as BEST ANSWER

    I finally managed to resolve the issue by moving the callbackUrl logic into the onSubmit method:

    const onSubmit = (values: z.infer<typeof LoginSchema>) => {
        setError("")
        setSuccess("")
        startTransition(() => {
          const currentUrl = `/${window.location.href
            .split("/")
            .slice(3)
            .join("/")}` // Get the current URL slug
          const originUrl = callbackUrl !== null ? callbackUrl : currentUrl
          login(values, originUrl)
            .then((data) => {
              if (data?.error) {
                form.reset()
                setError(data.error)
              }
              if (data?.success) {
                form.reset()
                setSuccess(data.success)
              }
              if (data?.twoFactor) {
                setShowTwoFactor(true)
              }
            })
            .catch(() => setError("Something went wrong"))
        })
      }
    

    This way, the URL is retrieved and slug extracted at the time of form submission rather than component mount or render. Using an if(), I test whether there’s a searchParam value. If yes, I send that. But if not, I send the computed currentUrl value.


  2. Just because the component is marked with use client directive doesn’t mean it doesn’t render on the server as well as the client.

    When client components are used, pre-rendered static HTML is still generated on the server to optimise the initial payload. That will be why you are seeing the error in the server logs.

    The docs explain the rendering process here https://nextjs.org/docs/app/building-your-application/rendering/client-components#how-are-client-components-rendered

    When using window object, it always needs to be in a place where it is only executed on the client, like a useEffect or event handler

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search