Auth0 Single Page App Confusion

Hi again!

Great to hear you managed to make progress with the indefinite loading screen created by the protected page!

Just to confirm, have you imported the respective createAuth0Client() within your application as such:

import { createAuth0Client } from '@auth0/auth0-spa-js';

From what I understand the root cause of the error message “createAuth0Client is not defined” would be due to the fact that it is not being imported from the Auth0 SPA JS sdk within your codebase, causing the whole authorization process to fail due to the severed connection to your Auth0 application and tenant.

If you can either post your code within the thread (make sure to sanitize any sensitive information) or you can send me a DM.

Kind Regards,
Nik

Kind Regards,
Nik

Hi Nik,

Let me explain.

In an earlier post you made this comment to me:

So I tried to use your suggestion to build a version of my app that would work on my GoDaddy server.

  1. I inserted a CDN script tag into the index.html file in the . I used your script that you provided in an earlier posting.
  2. I removed the Import Auth0 SPA JS from the app.js file and modified the Auth0Client code as follows:

let auth0Client = null;
window.onload = async () => {
auth0Client = await auth0.createAuth0Client({
domain: “YOUR_AUTH0_DOMAIN”,
clientId: “YOUR_CLIENT_ID”,
authorizationParams: {
redirect_uri: window.location.origin
}
});

I tried to run “npm run build” in my terminal but I received this error message in my terminal:

PS C:\PECBMembersProd> npm run build

pecbmembersprod@1.0.0 build
vite build
vite v8.0.13 building client environment for production…

✓ 5 modules transformed.
✗ Build failed in 269ms
error during build:

Build failed with 1 error:
[PARSE_ERROR] Expected a semicolon or an implicit semicolon after a statement, but found none
╭─[ app.js:31:4 ]

31 │ } catch (err) {
│ │
│ ╰─

│ Help: Try inserting a semicolon here
────╯

at aggregateBindingErrorsIntoJsError (file:///C:/PECBMembersProd/node_modules/rolldown/dist/shared/error-CkdMJ9ps.mjs:48:18)
at unwrapBindingResult (file:///C:/PECBMembersProd/node_modules/rolldown/dist/shared/error-CkdMJ9ps.mjs:18:128)
at #build (file:///C:/PECBMembersProd/node_modules/rolldown/dist/shared/rolldown-build-BVD3dIdE.mjs:3275:34)
at async buildEnvironment (file:///C:/PECBMembersProd/node_modules/vite/dist/node/chunks/node.js:33133:64)
at async Object.build (file:///C:/PECBMembersProd/node_modules/vite/dist/node/chunks/node.js:33555:19)
at async Object.buildApp (file:///C:/PECBMembersProd/node_modules/vite/dist/node/chunks/node.js:33552:153)
at async CAC.<anonymous> (file:///C:/PECBMembersProd/node_modules/vite/dist/node/cli.js:777:3) {

errors: [Getter/Setter]
}

Apparently Vite doesn’t like the syntax that you need to load Auth0 SPA JS when you’re running in a browser environment.

Is there a way to work around these issues and use Vite to build the code that does run in a browser?

Kind regards,

Warren

Hi again @wsmetz72

I am sorry for my delayed response, I have been out of office and could not get back to you in time.

I see that you are having issues in building a Single Page Application (SPA) using Auth0’s quickstart guide. You have successfully created a login page and authenticated test users. Now you’re trying to integrate my custom HTML page into the Auth0 app so it displays as a members-only page on my organization’s website.

[ROOT CAUSE]**

The confusion stems from mixing two incompatible approaches:

  1. CDN-based approach (older): Uses <script src="https://cdn.auth0.com/..."></script> and attaches auth0 to the global window object.
  2. Vite/NPM-based approach (newer): Uses ES Module imports and requires a build step.

The split-screen issue occurs because the example’s Universal Login component is still rendering on top of your custom content. This is a layout/CSS issue, not an Auth0 configuration issue.

The import error happens because you’re trying to use ES Module syntax (import) in a non-module context (likely PowerShell or a script that doesn’t support ES Modules).

[SOLUTION]

Step 1: Choose your approach — Vite/NPM (Recommended)

The newer Vite-based approach is recommended for modern SPAs. It provides better performance, security, and tooling.

Step 2: Install the Auth0 SDK via NPM

npm install @auth0/auth0-spa-js

Step 3: Remove the CDN link from index.html

Delete this line entirely:

<!-- REMOVE THIS -->
<script src="https://cdn.auth0.com/..."></script>

Replace it with a single module script tag:

<script type="module" src="/app.js"></script>

Step 4: Import the SDK correctly in app.js

At the very top of your app.js file, add the explicit import:

import { createAuth0Client } from '@auth0/auth0-spa-js';

let auth0Client;

window.onload = async () => {
    auth0Client = await createAuth0Client({
        domain: import.meta.env.VITE_AUTH0_DOMAIN,
        clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
        authorizationParams: {
            redirect_uri: window.location.origin
        }
    });
    
    // Check if user is logged in
    const isAuthenticated = await auth0Client.isAuthenticated();
    if (!isAuthenticated) {
        // Show login or redirect
        await auth0Client.loginWithRedirect();
    } else {
        // Show your custom member-only content
        document.getElementById('app').innerHTML = '<h1>Welcome, member!</h1>';
    }
};

Step 5: Fix the split-screen issue

The split-screen occurs because the Universal Login component is still being rendered. Instead of replacing content, conditionally show your page only after authentication:

// Hide Universal Login after successful authentication
const isAuthenticated = await auth0Client.isAuthenticated();

if (isAuthenticated) {
    // Hide login UI, show your custom content
    document.getElementById('auth0-login-container').style.display = 'none';
    document.getElementById('member-content').style.display = 'block';
} else {
    // Show login UI
    document.getElementById('auth0-login-container').style.display = 'block';
    document.getElementById('member-content').style.display = 'none';
}

Step 6: Build and deploy (without renaming files)

npm run build

Vite generates a dist folder with:

  • index.html (updated with correct file references)
  • assets/ folder (containing hashed files like app-rlbWHTKs.js)

Upload the entire dist folder to your hosting provider (e.g., GoDaddy). Do not rename any files.

When you run npm run build, Vite intentionally appends a random hash to file names (e.g., app-rlbWHTKs.js). This is called Cache Busting and serves two purposes:

  1. Browser caching: When you deploy updates, the new hash forces browsers to download the new file instead of using a cached version.
  2. Automatic linking: Vite automatically updates all references in index.html to point to the hashed files.

If you manually rename app-rlbWHTKs.js back to app.js, you break the links that Vite created in index.html, resulting in a blank page or broken application.

COMMON MISTAKES TO AVOID

Kind Regards,
Nik