You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next.js 13中持续渲染Google Identity Services按钮?

Next.js 13 App Router中Google登录按钮渲染问题

我用Next.js 13的App Router构建应用,通过Google提供的HTML+脚本模板添加了「使用Google登录」功能。一开始把按钮直接放在Auth表单里,后来将登录、注册拆成不同页面,共享AuthForm组件并通过页面传参切换状态,但切换页面(重定向后重新渲染)时Google按钮消失了——页面上有渲染按钮的div和脚本,但div是空的,脚本没重新执行。

我判断原因是Google按钮不是Next/React组件,而是靠异步脚本填充div;页面重渲染时,Next/React认为div已经存在,不会重新触发脚本执行。

后来我把脚本放到account/layout.tsx里,把按钮拆成独立组件和子组件分开渲染,表单切换的问题解决了,但用户从登录页导航到首页再返回时,按钮还是没法渲染。

当前相关代码如下:

account/layout.tsx

import Script from 'next/script';
import GoogleButton from '@/app/_components/GoogleButton';

export default function AccountLayout(
  { children, }:
    { children: React.ReactNode }
) {

  return (
    <section>
      <Script src="https://accounts.google.com/gsi/client" async></Script>
      {children}
      <GoogleButton />
    </section>
  )
}

account/page.tsx

import SignIn from "./signin/page";

export default function Account() {
  return (
    <div>
      <SignIn />
    </div>
  )
}

account/signin/page.tsx

import AuthForm from '@/app/_components/AuthForm';

export default function SignIn() {
  return (
    <>
      <AuthForm />
    </>
  )
}

_components/GoogleButton.tsx

import Container from "@mui/material/Container"

export default function GoogleButton() {
  return (
    <>
      <Container
        id="google-signin-container"
        sx={{
          border: "1px solid darkgrey",
          borderTop: "none",
          paddingTop: "10px",
          paddingBottom: "40px",
          width: "80%",
          display: "flex",
          flexDirection: "column",
          justifySelf: "center",
          alignItems: "center"
        }}>

        <div id="g_id_onload"
          data-client_id={`${process.env.GOOGLE_CLIENT_ID}`}
          data-context="signin"
          data-ux_mode="popup"
          data-login_uri="http://localhost:3000/"
          data-nonce=""
          data-itp_support="true">
        </div>
        <div className="g_id_signin"
          data-type="standard"
          data-shape="rectangular"
          data-theme="outline"
          data-text="signin_with"
          data-size="large"
          data-logo_alignment="left">
        </div>
      </Container>
    </>
  )
}
解决方案

通过以下方法解决了问题,还避免了直接访问window对象上的google属性:

核心思路是利用Next.js的useEffect钩子,在组件挂载或路由变化时,手动调用Google身份验证脚本提供的渲染方法,确保按钮每次都能正确初始化:

  1. 调整GoogleButton组件,添加useEffect监听组件挂载和路由变化:
import Container from "@mui/material/Container";
import { useEffect } from "react";
import { usePathname } from "next/navigation";

export default function GoogleButton() {
  const pathname = usePathname();

  useEffect(() => {
    // 检查Google脚本是否已加载完成
    const initializeGoogleButton = () => {
      if (typeof window !== "undefined" && window.google?.accounts?.id) {
        // 先销毁已存在的实例,避免重复渲染
        window.google.accounts.id.cancel();
        // 重新渲染Google登录按钮
        window.google.accounts.id.renderButton(
          document.querySelector(".g_id_signin")!,
          {
            type: "standard",
            shape: "rectangular",
            theme: "outline",
            text: "signin_with",
            size: "large",
            logo_alignment: "left"
          }
        );
        // 同时初始化配置
        window.google.accounts.id.initialize({
          client_id: `${process.env.GOOGLE_CLIENT_ID}`,
          context: "signin",
          ux_mode: "popup",
          login_uri: "http://localhost:3000/",
          itp_support: true
        });
      }
    };

    // 如果脚本已加载,直接初始化;否则监听加载事件
    if (window.google?.accounts?.id) {
      initializeGoogleButton();
    } else {
      const script = document.querySelector('script[src="https://accounts.google.com/gsi/client"]');
      if (script) {
        script.addEventListener("load", initializeGoogleButton);
        return () => script.removeEventListener("load", initializeGoogleButton);
      }
    }
  }, [pathname]); // 路由变化时重新执行

  return (
    <>
      <Container
        id="google-signin-container"
        sx={{
          border: "1px solid darkgrey",
          borderTop: "none",
          paddingTop: "10px",
          paddingBottom: "40px",
          width: "80%",
          display: "flex",
          flexDirection: "column",
          justifySelf: "center",
          alignItems: "center"
        }}>
        <div className="g_id_signin"></div>
      </Container>
    </>
  );
}
  1. 移除原GoogleButton组件中的g_id_onload div,因为配置已通过代码初始化,避免重复配置冲突。

这样调整后,无论是页面切换还是路由导航返回,Google登录按钮都会重新初始化渲染,解决了消失的问题。

内容的提问来源于stack exchange,提问作者hjdjoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:01:35