Zeros and OnesLLC

title: "React / Next.js Quickstart" description: "Get started with TitaniumVault authentication in your React or Next.js application in under 10 minutes." order: 1

React / Next.js Quickstart

This guide will walk you through adding TitaniumVault authentication to your React or Next.js application.

Prerequisites

  • Node.js 18+ installed
  • A TitaniumVault account and application configured
  • React 18+ or Next.js 13+ project

Installation

Install the TitaniumVault React SDK:

npm install @titaniumvault/react

Configuration

1. Set up environment variables

Create a .env.local file in your project root:

NEXT_PUBLIC_TV_DOMAIN=your-org.titanium-vault.com
NEXT_PUBLIC_TV_CLIENT_ID=your-client-id
NEXT_PUBLIC_TV_REDIRECT_URI=http://localhost:3000/callback

2. Wrap your app with the provider

For Next.js App Router, update your app/layout.tsx:

import { TitaniumVaultProvider } from '@titaniumvault/react';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <TitaniumVaultProvider
          domain={process.env.NEXT_PUBLIC_TV_DOMAIN!}
          clientId={process.env.NEXT_PUBLIC_TV_CLIENT_ID!}
          redirectUri={process.env.NEXT_PUBLIC_TV_REDIRECT_URI!}
        >
          {children}
        </TitaniumVaultProvider>
      </body>
    </html>
  );
}

For React (Vite or CRA), update your main.tsx:

import { TitaniumVaultProvider } from '@titaniumvault/react';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <TitaniumVaultProvider
    domain={import.meta.env.VITE_TV_DOMAIN}
    clientId={import.meta.env.VITE_TV_CLIENT_ID}
    redirectUri={import.meta.env.VITE_TV_REDIRECT_URI}
  >
    <App />
  </TitaniumVaultProvider>
);

Add Login/Logout

Create a login button component:

'use client';

import { useAuth } from '@titaniumvault/react';

export function LoginButton() {
  const { isAuthenticated, isLoading, loginWithRedirect, logout, user } = useAuth();

  if (isLoading) {
    return <button disabled>Loading...</button>;
  }

  if (isAuthenticated) {
    return (
      <div>
        <span>Welcome, {user?.name}</span>
        <button onClick={() => logout({ returnTo: window.location.origin })}>
          Logout
        </button>
      </div>
    );
  }

  return (
    <button onClick={() => loginWithRedirect()}>
      Login
    </button>
  );
}

Handle the Callback

For Next.js App Router, create app/callback/page.tsx:

'use client';

import { useEffect } from 'react';
import { useAuth } from '@titaniumvault/react';
import { useRouter } from 'next/navigation';

export default function CallbackPage() {
  const { handleRedirectCallback, isLoading, error } = useAuth();
  const router = useRouter();

  useEffect(() => {
    const handleCallback = async () => {
      try {
        await handleRedirectCallback();
        router.push('/dashboard');
      } catch (err) {
        console.error('Callback error:', err);
      }
    };

    handleCallback();
  }, [handleRedirectCallback, router]);

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return <div>Processing login...</div>;
}

Protect Routes

Create a protected route wrapper:

'use client';

import { useAuth } from '@titaniumvault/react';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';

export function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { isAuthenticated, isLoading, loginWithRedirect } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (!isLoading && !isAuthenticated) {
      loginWithRedirect();
    }
  }, [isLoading, isAuthenticated, loginWithRedirect]);

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!isAuthenticated) {
    return null;
  }

  return <>{children}</>;
}

Use it in your protected pages:

import { ProtectedRoute } from '@/components/ProtectedRoute';

export default function DashboardPage() {
  return (
    <ProtectedRoute>
      <h1>Dashboard</h1>
      {/* Protected content */}
    </ProtectedRoute>
  );
}

Calling APIs with Access Tokens

Get an access token for API calls:

'use client';

import { useAuth } from '@titaniumvault/react';

export function ApiComponent() {
  const { getAccessToken } = useAuth();

  const callApi = async () => {
    const token = await getAccessToken();

    const response = await fetch('https://api.example.com/data', {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });

    const data = await response.json();
    console.log(data);
  };

  return <button onClick={callApi}>Call API</button>;
}

Next Steps

Troubleshooting

"Redirect URI mismatch" error

Ensure your redirect URI in the TitaniumVault dashboard exactly matches NEXT_PUBLIC_TV_REDIRECT_URI, including the protocol and trailing slashes.

Token not refreshing

The SDK automatically handles token refresh. If you're experiencing issues, check that your refresh token settings are configured correctly in the TitaniumVault dashboard.

CORS errors

If making API calls from the browser, ensure your API is configured to accept requests from your application's origin.