1

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
2
  • Have a look at this vue-google-login-button-directive Commented Jun 5, 2023 at 12:50
  • That's interesting, does this mean there's some incompatibility between Vue and Google Auth? I'm trying to replicate the error and it seems like Vue Router is the issue. Commented Jun 5, 2023 at 15:33

2 Answers 2

5

This is how I have solved it, by replacing the script tag with the component one. You can take this code and directly use it as a Vue component.

See accepted answer here.

<template>
<div class="login-provider-google">
  <component is="script" src="https://accounts.google.com/gsi/client" async />
  <div id="g_id_onload"
    data-client_id="..."
    data-context="signin"
    data-ux_mode="popup"
    data-login_uri="..."
    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>
</div>
</template>

Not to self promote but after the fact I ended up writing a more comprehensive answer in my blog. In summary, use the JavaScript approach if you need a callback, and then use a combination of the onMounted hook and the onLoad event to initialise the button and prompt.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

If you are still having issues see my answer below

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.