Search code examples
vue.jsgoogle-oauthvue-routergoogle-signingoogle-identity

Google OAuth Sign In button does not render in Vue 3 using Vue Router


Problem

I'm trying to create a Google Sign In OAuth button in my Vue 3 app with Vue Router by following the official Google Docs: https://developers.google.com/identity/gsi/web/guides/display-button

I completed the setup, finished the OAuth Consent Screen, obtained my CLIENT_ID, have my login uri for the callback, imported the in my index.html file in Vue public folder.

For some reason, the button does not render in my Vue Router Views.

App Architecture

Vue 3, Composition API, Vue Router

  • GoogleAuthSignIn.vue is a component
  • SignUpView.vue is a view (Loaded by Vue Router) that imports and loads the GoogleAuthSignIn.vue component

Code

GoogleAuthSignIn.vue component displaying the button inside my Vue template. My CLIENT_ID is of course the actual one from my API

<template>
    <div id="g_id_onload"
        data-client_id="xxxxxx-xxxxxxxxxxxxxxxx.apps.googleusercontent.com"
        data-context="signin"
        data-ux_mode="popup"
        data-login_uri="https://localhost:3000/api/auth/oauth/google"
        data-itp_support="true">
    </div>

    <div class="g_id_signin"
        data-type="standard"
        data-shape="rectangular"
        data-theme="outline"
        data-text="signin_with"
        data-size="large"
        data-logo_alignment="left">
    </div>
</template>

I've also included the following inside the head of the index.html inside the "public" folder:

<script src="https://accounts.google.com/gsi/client" async defer></script>

Strange Behavior

I notice that sometimes very rarely it suddenly loads but I have no idea why. For example, I can refresh the page 50 times, and one of those times it will sometimes show.

I was reading this might be a timing issue of the script not loading, or that the script needs to load before the button, or button needs to load before the script. I am pretty confused as to why this happens.

Update 1

After tinkering some more, I realized that I can show / render the button if I place the HTML render code directly inside the App.vue file.

It also works when I create a component that has the g_id_onload and g_id_signin divs, and import that component into App.vue.

However, it doesn't work inside my SignUpView.vue that loads via the Vue Router.

*Is this an issue where it is not compatible with Vue Router?

Update 2

I tried replicating the issue and found out that there is a problem with loading the Google Sign In Buttons inside of Vue Router Views.

  • Google Sign In buttons render when loaded directly in App.vue
  • Google Sign In buttons do NOT render when loaded into a View, which is then loaded into App.vue

Solution

  • Solution

    Import your view at the top of your Vue Router index.js file

    import SignUpView from '../views/SignUpView.vue'
    

    Use normal view imports and write your route like this:

    {
        path: '/',
        name: 'signUpView',
        component: SignUpView
    }
    

    Error

    Do Not Use the Code-Splitting way of writing your route:

    {
        path: '/',
        name: 'landing',
        component: () => import ('../views/SignUpView.vue')
    }
    

    Why

    Route level code-splitting is lazy-loaded when the route is visited. This means that not all resources are loaded when the view is rendered, and resources are loaded only when they are needed.

    From my understanding, this causes an issue with loading the Google GSI Script in the index.html file in the public folder:

    <script src="https://accounts.google.com/gsi/client" async defer></script>
    

    If there is another way or better explanation of the issue feel free to add one as I would also be curious if there is another possible solution that combines lazy-loading and loading the google scripts.