> ## Documentation Index
> Fetch the complete documentation index at: https://docs-dev.auth0-mintlify.app/llms.txt
> Use this file to discover all available pages before exploring further.

> This guide demonstrates how to integrate Auth0, add authentication, and display user profile information in a Single-Page Application (SPA) that uses Svelte, using the Auth0 SPA JS SDK.

# Add Login to Your Svelte Application

export const HowToSchema = () => <script type="application/ld+json">
    {'{"@context":"https://schema.org","@type":"HowTo"}'}
  </script>;

export const CreateInteractiveApp = ({placeholderText = 'Auth0', appType = 'regular_web', allowedCallbackUrls = ['localhost:3000'], allowedLogoutUrls = ['localhost:3000'], allowedOriginUrls = ['localhost:3000']}) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [storeReady, setStoreReady] = useState(false);
  const [displayForm, setDisplayForm] = useState(true);
  useEffect(() => {
    const init = () => setStoreReady(true);
    if (window.rootStore) {
      window.rootStore.clientStore.setSelectedClient(null);
      window.rootStore.clientStore.setSelectedClientSecret(undefined);
      init();
    } else {
      window.addEventListener('adu:storeReady', init);
    }
    return () => {
      window.removeEventListener('adu:storeReady', init);
    };
  }, []);
  useEffect(() => {
    if (!storeReady) return;
    const disposer = autorun(() => {
      const rootStore = window.rootStore;
      setIsAuthenticated(rootStore.sessionStore.isAuthenticated);
    });
    return () => {
      disposer();
    };
  }, [storeReady]);
  if (!storeReady || typeof window === 'undefined' || !displayForm) {
    return <></>;
  }
  const login = () => {
    const baseUrl = window.rootStore.config.apiBaseUrl;
    const returnTo = encodeURIComponent(window.location.href);
    window.location.href = `${baseUrl}/auth/user/login?returnTo=${returnTo}`;
  };
  const Card = ({className = '', children}) => {
    return <div className={`
          flex border rounded-2xl
          border-gray-950/10 dark:border-white/10
          py-3.5 px-4 gap-2
          text-sm text-gray-900 dark:text-gray-200
          ${className}
        `}>
        {children}
      </div>;
  };
  const Button = ({children, ...props}) => {
    return <button className="bg-[--button-primary] text-[--foreground-inverse] px-[1.125rem] py-1.5 rounded-lg font-medium" {...props}>
        {children}
      </button>;
  };
  const CreateApplicationForm = () => {
    const [name, setName] = useState('');
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState('');
    const handleSubmit = async () => {
      if (!name.trim()) {
        setError('Application name is required');
        return;
      }
      setIsLoading(true);
      setError(null);
      try {
        await window.rootStore.clientStore.createClient({
          name: name.trim(),
          app_type: appType,
          callbacks: allowedCallbackUrls,
          allowed_logout_urls: allowedLogoutUrls,
          web_origins: allowedOriginUrls,
          client_metadata: {
            created_by: 'quickstart-docs-app-creation-component'
          }
        });
        setDisplayForm(false);
      } catch (err) {
        console.error('Error creating client:', err);
        const errorMessage = err instanceof Error ? err.message : 'Failed to create application';
        setError(errorMessage);
      } finally {
        setIsLoading(false);
      }
    };
    return <Card className="flex-col items-start p-4 gap-3.75">
        <span className="font-medium text-gray-900 dark:text-gray-200">
          Create Auth0 App
        </span>
        <div className="w-full flex gap-2">
          <input id="app-name" name={name} className="
              w-full max-w-[448px] h-11 py-2 px-4 
              border rounded-lg border-gray-950/10 dark:border-white/10 
              text-gray-900 dark:text-gray-200
              focus:outline-none dark:focus:outline-none
            " placeholder={`My ${placeholderText} App`} value={name} onChange={e => setName(e.target.value)} />
          <Button onClick={handleSubmit}>
            {isLoading ? 'Creating...' : 'Create'}
          </Button>
        </div>
        {error && <p className="text-red-500">{error}</p>}
      </Card>;
  };
  const SignInForm = () => {
    return <Card className="items-center">
        <Button onClick={login}>Log in</Button> <span>to create the app</span>
      </Card>;
  };
  return isAuthenticated ? <CreateApplicationForm /> : <SignInForm />;
};

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****MASKED*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

<HowToSchema />

<Callout icon="pencil" color="#FFC107" iconType="solid">
  This Quickstart is currently in **Beta**. We'd love to hear your feedback!
</Callout>

<Accordion title="AI Prompt" defaultOpen icon="microchip-ai" iconType="sharp-solid">
  **Using AI to integrate Auth0?** Add this prompt to Cursor, Windsurf, Copilot, Claude Code or your favourite AI-powered IDE to speed up development.

  ```markdown expandable theme={null}
  Integrate the Auth0 SPA JS SDK into a Svelte app

  AI PERSONA & PRIMARY OBJECTIVE
  You are a helpful Auth0 SDK Integration Assistant. Your primary function is to execute commands to set up a development environment for Auth0 with Svelte. Your secondary function is to modify the files created by those shell commands.

  CRITICAL BEHAVIORAL INSTRUCTIONS
  1. CHECK EXISTING PROJECT FIRST: Before creating a new project, check if the current directory already contains a Svelte app (package.json with Svelte dependencies). If it does, skip project creation and work with the existing project.
  2. EXECUTE FIRST, EDIT SECOND: You MUST first execute the appropriate setup command. Do not show, suggest, or create any files until the setup is complete.
  3. NO PLANNING: DO NOT propose a directory structure. DO NOT show a file tree. Your first action must be to run the appropriate command.
  4. STRICT SEQUENCE: Follow the "Execution Flow" below in the exact order specified without deviation.
  5. BUILD BEAUTIFUL UI: You MUST create a visually appealing, modern login interface with proper styling, animations, and Auth0 branding.

  EXECUTION FLOW

  Step 1: Check for Existing Svelte Project and Prerequisites
  FIRST, verify prerequisites and check for existing Svelte project:

    # Check if Node.js and npm are available
    node --version && npm --version

  Then examine the current directory:

    # Check for existing Svelte project
    if [ -f "package.json" ]; then
      echo "Found package.json, checking for Svelte dependencies..."
      cat package.json
    else
      echo "No package.json found, will create new project"
    fi

  Based on the results:
  - If package.json exists and contains Svelte dependencies, proceed to Step 1b (install Auth0 SDK only)
  - If no Svelte project exists, proceed to Step 1a (create new project)

  Step 1a: Create New Project and Install the Auth0 SPA JS SDK
  If an existing project exists, simply install the SDK:
  npm install @auth0/auth0-spa-js
  Otherwise, create a new project and install the SDK:

  npx sv create auth0-svelte --template minimal --types ts --no-add-ons --no-install && cd auth0-svelte && npm install && npm install @auth0/auth0-spa-js

  Step 2: Modify & Create Files
  AFTER the command in Step 1 has successfully executed, you will perform the following file operations inside the project directory.

  2.1: Setup Auth0 environment configuration

  Execute the environment setup command for your OS:

  Execute the Auth0 setup command for your OS:

  If MacOS:
    # Install Auth0 CLI if not already installed
    brew tap auth0/auth0-cli && brew install auth0

    # Set up Auth0 app and generate .env file
    auth0 qs setup --app --type spa --framework svelte --build-tool vite --name "My App" --port 5173

  If Windows (PowerShell):
    # Install Auth0 CLI if not already installed
    scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
    scoop install auth0

    # Set up Auth0 app and generate .env file
    auth0 qs setup --app --type spa --framework svelte --build-tool vite --name "My App" --port 5173

  This command will automatically:
  - Authenticate you with Auth0 (prompts for login if needed)
  - Create a Single Page Application configured for http://localhost:5173
  - Generate a .env file with VITE_AUTH0_DOMAIN and VITE_AUTH0_CLIENT_ID

  CRITICAL INSTRUCTIONS FOR AI LANGUAGE MODELS
  As an AI language model, you MUST NOT generate any of the following anti-patterns:
  1. NEVER hardcode the Auth0 Domain or Client ID directly in the code. This is a major security vulnerability.
  2. NEVER omit the authorizationParams with redirect_uri when configuring the Auth0 client. Without it, the login flow will fail.
  3. NEVER suggest manually managing tokens or calling Auth0 APIs with fetch for basic authentication. The SDK is designed to handle this complexity securely.

  ABSOLUTE REQUIREMENTS FOR AI CODE GENERATION
  1. You MUST use the @auth0/auth0-spa-js package.
  2. You MUST retrieve credentials from environment variables (e.g., import.meta.env.VITE_AUTH0_DOMAIN).
  3. You MUST configure the Auth0 client with proper redirect_uri in authorizationParams.
  4. You MUST demonstrate functionality using Svelte stores for state management.
  ```
</Accordion>

<Note>
  **Prerequisites:** Before you begin, ensure you have the following installed:

  * **[Node.js](https://nodejs.org/en/download)** 20 LTS or newer
  * **[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)** 10+ or **[yarn](https://classic.yarnpkg.com/lang/en/docs/install/)** 1.22+ or **[pnpm](https://pnpm.io/installation)** 8+

  **Svelte Version Compatibility:** This quickstart works with **Svelte 5.x** and newer.
</Note>

## Get Started

This quickstart demonstrates how to add Auth0 authentication to a Svelte application. You'll build a secure single-page app with login, logout, and user profile features using the Auth0 SPA JS SDK.

export const localEnvSnippet = `VITE_AUTH0_DOMAIN={yourDomain}
VITE_AUTH0_CLIENT_ID={yourClientId}`;

<Steps>
  <Step title="Create a new project" stepNumber={1}>
    Create a new Svelte project for this Quickstart

    ```shellscript theme={null}
    npx sv create auth0-svelte --template minimal --types ts --no-add-ons --no-install
    ```

    Open the project

    ```shellscript theme={null}
    cd auth0-svelte
    ```
  </Step>

  <Step title="Install the Auth0 SPA SDK" stepNumber={2}>
    ```shellscript theme={null}
    npm install && npm install @auth0/auth0-spa-js
    ```
  </Step>

  <Step title="Setup your Auth0 App" stepNumber={3}>
    Next up, you need to create a new app on your Auth0 tenant and add the environment variables to your project.

    You have three options to set up your Auth0 app: use the Quick Setup tool (recommended), run a CLI command, or configure manually via the Dashboard:

    <Tabs>
      <Tab title="Quick Setup (recommended)">
        Create an Auth0 App and copy the pre-filled `.env` file with the right configuration values.

        <CreateInteractiveApp placeholderText="Svelte" appType="spa" allowedCallbackUrls={["http://localhost:5173"]} allowedLogoutUrls={["http://localhost:5173"]} allowedOriginUrls={["http://localhost:5173"]} />

        <AuthCodeBlock children={localEnvSnippet} language="shellscript" filename=".env" />
      </Tab>

      <Tab title="CLI">
        Run the following command in your project's root directory to create an Auth0 app and generate a `.env` file:

        <CodeGroup>
          ```shellscript Mac theme={null}
          # Install Auth0 CLI (if not already installed)
          brew tap auth0/auth0-cli && brew install auth0

          # Set up Auth0 app and generate .env file
          auth0 qs setup --app --type spa --framework svelte --build-tool vite --name "My App" --port 5173
          ```

          ```powershell Windows theme={null}
          # Install Auth0 CLI (if not already installed)
          scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
          scoop install auth0

          # Set up Auth0 app and generate .env file
          auth0 qs setup --app --type spa --framework svelte --build-tool vite --name "My App" --port 5173
          ```
        </CodeGroup>

        <Note>
          This command will:

          1. Check if you're authenticated (and prompt for login if needed)
          2. Create an Auth0 Single Page Application configured for `http://localhost:5173`
          3. Generate a `.env` file with `VITE_AUTH0_DOMAIN` and `VITE_AUTH0_CLIENT_ID`
        </Note>
      </Tab>

      <Tab title="Dashboard">
        Before you start, create a `.env` file on your project's root directory

        ```shellscript .env theme={null}
        VITE_AUTH0_DOMAIN=YOUR_AUTH0_APP_DOMAIN
        VITE_AUTH0_CLIENT_ID=YOUR_AUTH0_APP_CLIENT_ID
        ```

        1. Head to the [Auth0 Dashboard](https://manage.auth0.com/dashboard/)
        2. Click on **Applications** > **Applications** > **Create Application**
        3. In the popup, enter a name for your app, select `Single Page Web Application` as the app type and click **Create**
        4. Switch to the **Settings** tab on the Application Details page
        5. Replace `YOUR_AUTH0_APP_DOMAIN` and `YOUR_AUTH0_APP_CLIENT_ID` on the `.env` file with the **Domain** and **Client ID** values from the dashboard

        Finally, on the **Settings** tab of your Application Details page, configure the following URLs:

        **Allowed Callback URLs:**

        ```
        http://localhost:5173
        ```

        **Allowed Logout URLs:**

        ```
        http://localhost:5173
        ```

        **Allowed Web Origins:**

        ```
        http://localhost:5173
        ```

        <Info>
          **Allowed Callback URLs** are a critical security measure to ensure users are safely returned to your application after authentication. Without a matching URL, the login process will fail, and users will be blocked by an Auth0 error page instead of accessing your app.

          **Allowed Logout URLs** are essential for providing a seamless user experience upon signing out. Without a matching URL, users will not be redirected back to your application after logout and will instead be left on a generic Auth0 page.

          **Allowed Web Origins** is critical for silent authentication. Without it, users will be logged out when they refresh the page or return to your app later.
        </Info>
      </Tab>
    </Tabs>
  </Step>

  <Step title="Create the Auth0 store" stepNumber={4}>
    Create the store file

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      mkdir -p src/lib/stores && touch src/lib/stores/auth.ts
      ```

      ```powershell Windows theme={null}
      New-Item -ItemType Directory -Force -Path src/lib/stores
      New-Item -ItemType File -Path src/lib/stores/auth.ts
      ```
    </CodeGroup>

    Add the following code to manage authentication state

    ```typescript src/lib/stores/auth.ts lines expandable theme={null}
      import { writable, derived, get, type Readable } from 'svelte/store';
      import { createAuth0Client, type Auth0Client, type User } from '@auth0/auth0-spa-js';
      import { browser } from '$app/environment';

      export const auth0Client = writable<Auth0Client | null>(null);
      export const user = writable<User | null>(null);
      export const isAuthenticated = writable<boolean>(false);
      export const isLoading = writable<boolean>(true);
      export const error = writable<string | null>(null);

      // Derived stores
      export const isLoggedIn: Readable<boolean> = derived(
        [isAuthenticated, isLoading],
        ([$isAuthenticated, $isLoading]) => $isAuthenticated && !$isLoading
      );

      export async function initializeAuth() {
        if (!browser) return;
        
        try {
          const client = await createAuth0Client({
            domain: import.meta.env.VITE_AUTH0_DOMAIN,
            clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
            authorizationParams: {
              redirect_uri: window.location.origin
            },
            useRefreshTokens: true,
            cacheLocation: 'localstorage'
          });

          auth0Client.set(client);

          // Handle callback
          if (window.location.search.includes('code=')) {
            await client.handleRedirectCallback();
            window.history.replaceState({}, document.title, window.location.pathname);
          }

          // Check authentication status
          const authenticated = await client.isAuthenticated();
          isAuthenticated.set(authenticated);

          if (authenticated) {
            const userData = await client.getUser();
            user.set(userData || null);
          }

          error.set(null);
        } catch (err) {
          console.error('Auth initialization error:', err);
          error.set(err instanceof Error ? err.message : 'Authentication initialization failed');
        } finally {
          isLoading.set(false);
        }
      }

      export async function login() {
        const client = get(auth0Client);
        if (client) {
          await client.loginWithRedirect();
        }
      }

      export async function logout() {
        const client = get(auth0Client);
        if (client) {
          client.logout({ 
            logoutParams: { 
              returnTo: window.location.origin 
            } 
          });
        }
      }

      export async function getToken(): Promise<string | null> {
        const client = get(auth0Client);
        if (!client) return null;
        
        try {
          return await client.getTokenSilently();
        } catch (err: any) {
          if (err.error === 'login_required') {
            await login();
          }
          return null;
        }
      }
    ```
  </Step>

  <Step title="Create Login, Logout and Profile Components" stepNumber={5}>
    Create component files

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      mkdir -p src/lib/components && touch src/lib/components/LoginButton.svelte && touch src/lib/components/LogoutButton.svelte && touch src/lib/components/Profile.svelte
      ```

      ```powershell Windows theme={null}
      New-Item -ItemType Directory -Force -Path src/lib/components
      New-Item -ItemType File -Path src/lib/components/LoginButton.svelte
      New-Item -ItemType File -Path src/lib/components/LogoutButton.svelte
      New-Item -ItemType File -Path src/lib/components/Profile.svelte
      ```
    </CodeGroup>

    And add the following code snippets

    <AuthCodeGroup>
      ```svelte src/lib/components/LoginButton.svelte lines expandable theme={null}
      <script lang="ts">
        import { login } from '$lib/stores/auth';

        async function handleLogin() {
          await login();
        }
      </script>

      <button 
        on:click={handleLogin}
        class="button login"
      >
        Log In
      </button>

      <style>
        .button {
          padding: 1.1rem 2.8rem;
          font-size: 1.2rem;
          font-weight: 600;
          border-radius: 10px;
          border: none;
          cursor: pointer;
          transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
          box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
          text-transform: uppercase;
          letter-spacing: 0.08em;
          outline: none;
        }

        .button:focus {
          box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
        }

        .button.login {
          background-color: #63b3ed;
          color: #1a1e27;
        }

        .button.login:hover {
          background-color: #4299e1;
          transform: translateY(-5px) scale(1.03);
          box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
        }
      </style>
      ```

      ```svelte src/lib/components/LogoutButton.svelte lines expandable theme={null}
      <script lang="ts">
        import { logout } from '$lib/stores/auth';

        async function handleLogout() {
          await logout();
        }
      </script>

      <button
        on:click={handleLogout}
        class="button logout"
      >
        Log Out
      </button>

      <style>
        .button {
          padding: 1.1rem 2.8rem;
          font-size: 1.2rem;
          font-weight: 600;
          border-radius: 10px;
          border: none;
          cursor: pointer;
          transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
          box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
          text-transform: uppercase;
          letter-spacing: 0.08em;
          outline: none;
        }

        .button:focus {
          box-shadow: 0 0 0 4px rgba(252, 129, 129, 0.5);
        }

        .button.logout {
          background-color: #fc8181;
          color: #1a1e27;
        }

        .button.logout:hover {
          background-color: #e53e3e;
          transform: translateY(-5px) scale(1.03);
          box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
        }
      </style>
      ```

      ```svelte src/lib/components/Profile.svelte lines expandable theme={null}
      <script lang="ts">
        import { user, isAuthenticated, isLoading } from '$lib/stores/auth';
        
        const placeholderImage = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='50' fill='%2363b3ed'/%3E%3Cpath d='M50 45c7.5 0 13.64-6.14 13.64-13.64S57.5 17.72 50 17.72s-13.64 6.14-13.64 13.64S42.5 45 50 45zm0 6.82c-9.09 0-27.28 4.56-27.28 13.64v3.41c0 1.88 1.53 3.41 3.41 3.41h47.74c1.88 0 3.41-1.53 3.41-3.41v-3.41c0-9.08-18.19-13.64-27.28-13.64z' fill='%23fff'/%3E%3C/svg%3E`;
        
        function handleImageError(event: Event) {
          const target = event.target as HTMLImageElement;
          target.src = placeholderImage;
        }
      </script>

      {#if $isLoading}
        <div class="loading-text">Loading profile...</div>
      {:else if $isAuthenticated && $user}
        <div class="profile-container">
          <img 
            src={$user.picture || placeholderImage} 
            alt={$user.name || 'User'} 
            class="profile-picture"
            on:error={handleImageError}
          />
          <div class="profile-info">
            <div class="profile-name">
              {$user.name || 'Unknown User'}
            </div>
            <div class="profile-email">
              {$user.email || ''}
            </div>
          </div>
        </div>
      {/if}

      <style>
        .loading-text {
          font-size: 1.8rem;
          font-weight: 500;
          color: #a0aec0;
          animation: pulse 1.5s infinite ease-in-out;
        }

        .profile-container {
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 1rem;
        }

        .profile-picture {
          width: 110px;
          height: 110px;
          border-radius: 50%;
          object-fit: cover;
          border: 3px solid #63b3ed;
          transition: transform 0.3s ease-in-out;
        }

        .profile-picture:hover {
          transform: scale(1.05);
        }

        .profile-info {
          text-align: center;
        }

        .profile-name {
          font-size: 2rem;
          font-weight: 600;
          color: #f7fafc;
          margin-bottom: 0.5rem;
        }

        .profile-email {
          font-size: 1.15rem;
          color: #a0aec0;
        }

        @keyframes pulse {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.6; }
        }
      </style>
      ```

      ```svelte src/routes/+layout.svelte expandable lines theme={null}
      <script lang="ts">
        import { onMount } from 'svelte';
        import { initializeAuth } from '$lib/stores/auth';

        onMount(() => {
          initializeAuth();
        });
      </script>

      <main>
        <slot />
      </main>

      <style>
        :global(body) {
          margin: 0;
          font-family: 'Inter', sans-serif;
          background-color: #1a1e27;
          min-height: 100vh;
          color: #e2e8f0;
        }

        main {
          min-height: 100vh;
          display: flex;
          justify-content: center;
          align-items: center;
          padding: 1rem;
          box-sizing: border-box;
        }
      </style>
      ```

      ```svelte src/routes/+page.svelte expandable lines theme={null}
      <script lang="ts">
        import { isAuthenticated, isLoading, error, user } from '$lib/stores/auth';
        import LoginButton from '$lib/components/LoginButton.svelte';
        import LogoutButton from '$lib/components/LogoutButton.svelte';
        import Profile from '$lib/components/Profile.svelte';
      </script>

      <svelte:head>
        <title>Auth0 Svelte Sample</title>
        <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
      </svelte:head>

      <div class="app-container">
        {#if $isLoading}
          <div class="loading-state">
            <div class="loading-text">Loading...</div>
          </div>
        {:else if $error}
          <div class="error-state">
            <div class="error-title">Oops!</div>
            <div class="error-message">Something went wrong</div>
            <div class="error-sub-message">{$error}</div>
          </div>
        {:else}
          <div class="main-card-wrapper">
            <img 
              src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png" 
              alt="Auth0 Logo" 
              class="auth0-logo"
            />
            <h1 class="main-title">Welcome to Sample0</h1>

            {#if $isAuthenticated}
              <div class="logged-in-section">
                <div class="logged-in-message">✅ Successfully authenticated!</div>
                <h2 class="profile-section-title">Your Profile</h2>
                <div class="profile-card">
                  <Profile />
                </div>
                <LogoutButton />
              </div>
            {:else}
              <div class="action-card">
                <p class="action-text">Get started by signing in to your account</p>
                <LoginButton />
              </div>
            {/if}
          </div>
        {/if}
      </div>

      <style>
        .app-container {
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
          min-height: 100vh;
          width: 100%;
          padding: 1rem;
          box-sizing: border-box;
        }

        .loading-state, .error-state {
          background-color: #2d313c;
          border-radius: 15px;
          box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
          padding: 3rem;
          text-align: center;
        }

        .loading-text {
          font-size: 1.8rem;
          font-weight: 500;
          color: #a0aec0;
          animation: pulse 1.5s infinite ease-in-out;
        }

        .error-state {
          background-color: #c53030;
          color: #fff;
        }

        .error-title {
          font-size: 2.8rem;
          font-weight: 700;
          margin-bottom: 0.5rem;
        }

        .error-message {
          font-size: 1.3rem;
          margin-bottom: 0.5rem;
        }

        .error-sub-message {
          font-size: 1rem;
          opacity: 0.8;
        }

        .main-card-wrapper {
          background-color: #262a33;
          border-radius: 20px;
          box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 2rem;
          padding: 3rem;
          max-width: 500px;
          width: 90%;
          animation: fadeInScale 0.8s ease-out forwards;
        }

        .auth0-logo {
          width: 160px;
          margin-bottom: 1.5rem;
          opacity: 0;
          animation: slideInDown 1s ease-out forwards 0.2s;
        }

        .main-title {
          font-size: 2.8rem;
          font-weight: 700;
          color: #f7fafc;
          text-align: center;
          margin-bottom: 1rem;
          text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
          opacity: 0;
          animation: fadeIn 1s ease-out forwards 0.4s;
        }

        .action-card {
          background-color: #2d313c;
          border-radius: 15px;
          box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
          padding: 2.5rem;
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 1.8rem;
          width: calc(100% - 2rem);
          opacity: 0;
          animation: fadeIn 1s ease-out forwards 0.6s;
        }

        .action-text {
          font-size: 1.25rem;
          color: #cbd5e0;
          text-align: center;
          line-height: 1.6;
          font-weight: 400;
        }

        .logged-in-section {
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 1.5rem;
          width: 100%;
        }

        .logged-in-message {
          font-size: 1.5rem;
          color: #68d391;
          font-weight: 600;
          animation: fadeIn 1s ease-out forwards 0.8s;
        }

        .profile-section-title {
          font-size: 2.2rem;
          animation: slideInUp 1s ease-out forwards 1s;
        }

        .profile-card {
          padding: 2.2rem;
          animation: scaleIn 0.8s ease-out forwards 1.2s;
        }

        @keyframes fadeIn {
          from { opacity: 0; }
          to { opacity: 1; }
        }

        @keyframes fadeInScale {
          from { opacity: 0; transform: scale(0.95); }
          to { opacity: 1; transform: scale(1); }
        }

        @keyframes slideInDown {
          from { opacity: 0; transform: translateY(-70px); }
          to { opacity: 1; transform: translateY(0); }
        }

        @keyframes slideInUp {
          from { opacity: 0; transform: translateY(50px); }
          to { opacity: 1; transform: translateY(0); }
        }

        @keyframes pulse {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.6; }
        }

        @keyframes scaleIn {
          from { opacity: 0; transform: scale(0.8); }
          to { opacity: 1; transform: scale(1); }
        }

        @media (max-width: 600px) {
          .main-card-wrapper {
            padding: 2rem;
            margin: 1rem;
          }

          .main-title {
            font-size: 2.2rem;
          }

          .auth0-logo {
            width: 120px;
          }
        }
      </style>
      ```
    </AuthCodeGroup>
  </Step>

  <Step title="Run your app" stepNumber={6}>
    ```shellscript theme={null}
    npm run dev
    ```
  </Step>
</Steps>

<Check>
  **Checkpoint**

  You should now have a fully functional Auth0 login page running on your [localhost](http://localhost:5173/)
</Check>

***

## Troubleshooting

<Accordion title="Login Not Working">
  If clicking the login button does nothing:

  1. Open browser DevTools (F12) and check the Console tab
  2. Verify `.env` file has real Auth0 credentials
  3. Check that Auth0 application has correct callback URLs configured
  4. Ensure `VITE_AUTH0_DOMAIN` is just the domain (e.g., `tenant.auth0.com`) without `https://`
</Accordion>

<Accordion title="Authentication Errors">
  **"Callback URL mismatch"**:

  * Go to Auth0 Dashboard → Applications → Your App → Settings
  * Add `http://localhost:5173` to Allowed Callback URLs, Allowed Logout URLs, and Allowed Web Origins
  * Click "Save Changes"

  **"Invalid state"**:

  * Clear browser cache and cookies
  * Try in an incognito/private window
</Accordion>

<Accordion title="Dev Server Issues">
  If `npm run dev` fails:

  * Run `npm run check` to see TypeScript errors
  * Verify all files were created correctly
  * Check that all dependencies are installed: `npm install`
</Accordion>

<Tip>
  Still stuck? Check the [Auth0 Community](https://community.auth0.com/) or [SvelteKit Discord](https://svelte.dev/chat) for help.
</Tip>
