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

# Integrate Hanko with React

> Learn how to quickly add authentication and user profile in your React app using Hanko.

<div class="hidden">
  **Hanko Frontend Integration Guide**:

  **About Hanko**:

  Hanko is a modern open source authentication solution and the fastest way you integrate passkeys, 2FA, SSO, and more—with full control over your data. Move between self-hosted and Hanko Cloud anytime. No lock-in. Just Auth how it should be: secure, user friendly, and fully yours.

  **What This Guide Covers**: This guide demonstrates how to integrate Hanko authentication into your frontend application. You'll implement user authentication, profile management, route protection, and logout functionality with Hanko Elements web components.

  **Key Technologies**:

  * Modern frontend framework with TypeScript support
  * Build tools and development environment
  * Client-side routing and navigation
  * Hanko Elements web components
  * Hanko SDK for authentication logic

  **Prerequisites**:

  * Node.js installed on your system
  * Basic knowledge of your chosen frontend framework
  * A Hanko Cloud account (sign up at cloud.hanko.io)

  **Integration Tasks You'll Complete**:

  * Set up your frontend application with the appropriate build tools
  * Install and configure Hanko Elements
  * Create a Hanko project in the cloud console
  * Implement authentication components (HankoAuth, HankoProfile)
  * Set up routing and navigation
  * Add logout functionality
  * Implement protected routes with session validation
  * Retrieve and display user data
  * Customize component styling and behavior
</div>

## Create a React application

Set up your React frontend using [Vite](https://vite.dev/) as the build tool.

Run the following command to [create a new Vite React application](https://vite.dev/guide/):

<CodeGroup>
  ```bash npm theme={null}
  npm create vite@latest project-name -- --template react-ts
  ```

  ```bash pnpm theme={null}
  pnpm create vite project-name --template react-ts
  ```

  ```bash bun theme={null}
  bun create vite project-name --template react-ts
  ```

  ```bash yarn theme={null}
  yarn create vite project-name --template react-ts
  ```
</CodeGroup>

## Install `@teamhanko/hanko-elements`

Install `hanko-elements` to access the pre-built `hanko-auth` and `hanko-profile` components.

Also install `react-router-dom` for navigation between pages and routes.

<CodeGroup>
  ```bash npm theme={null}
  cd project-name
  npm install @teamhanko/hanko-elements react-router-dom
  ```

  ```bash pnpm theme={null}
  cd project-name
  pnpm add @teamhanko/hanko-elements react-router-dom
  ```

  ```bash bun theme={null}
  cd project-name
  bun add @teamhanko/hanko-elements react-router-dom
  ```

  ```bash yarn theme={null}
  cd project-name
  yarn add @teamhanko/hanko-elements react-router-dom
  ```
</CodeGroup>

## Set up your Hanko project

Go to the [Hanko Console](https://cloud.hanko.io/) and [create a project for this application.](/setup-hanko-cloud)

<Note>
  During creation make sure to input the URL you will be developing on as the `APP URL`.
  (Most likely [http://localhost:5173/](http://localhost:5173/))
</Note>

## Add the Hanko API URL

Retrieve the API URL from the [Hanko Console](https://cloud.hanko.io/) and place it in your [.env file](https://vite.dev/guide/env-and-mode).

```sh .env theme={null}
VITE_HANKO_API_URL=https://f4****-4802-49ad-8e0b-3d3****ab32.hanko.io
```

<Note>
  If you are self-hosting you need to provide the URL of your running Hanko
  backend.
</Note>

## Create Hanko components

Create a folder called `components` and create two files in it, `HankoAuth.tsx` and `HankoProfile.tsx`.

### Typescript

To get these elements to work with typescript, currently we must add the types to the project.
To do do this, create a file called `custom.d.ts` and place it in your apps root / src folder.

```jsx src/custom.d.ts theme={null}
import { HankoAuthElementProps, HankoProfileElementProps, HankoEventsElementProps } from "@teamhanko/hanko-elements";

declare module "react" {
  namespace JSX {
    interface IntrinsicElements {
        "hanko-auth": HankoAuthElementProps;
        "hanko-login": HankoAuthElementProps;
        "hanko-registration": HankoAuthElementProps;
        "hanko-profile": HankoProfileElementProps;
        "hanko-events": HankoEventsElementProps;
    }
  }
}
```

### Hanko Auth

Now lets setup the `HankoAuth.tsx` file to create a functioning login page.

Here we subscribe to the `onSessionCreated` [event](/guides/hanko-elements/using-frontend-sdk#events), this triggers when a user successfully logs in. You can use these event to perform any desired action (e.g. redirect to your dashboard).

For more information please refer to the [Auth component page.](/guides/hanko-elements/auth-component)

```jsx components/HankoAuth.tsx theme={null}
import { useEffect, useCallback, useMemo } from "react";
import { useNavigate } from 'react-router-dom';
import { register, Hanko } from "@teamhanko/hanko-elements";

const hankoApi = import.meta.env.VITE_HANKO_API_URL;
 
export default function HankoAuth() {
  const navigate = useNavigate();
 
  const hanko = useMemo(() => new Hanko(hankoApi), []);
 
  const redirectAfterLogin = useCallback(() => {
    // redirect to a page in your application
    navigate("/dashboard");
  }, [navigate]);

  useEffect(
    () =>
      hanko?.onSessionCreated(() => {
       //succesfully logged in
        redirectAfterLogin();
      }),
    [hanko, redirectAfterLogin]
  );
 
  useEffect(() => {
    register(hankoApi).catch((error) => {
      // handle error
      console.log(error)
    });
  }, []);
 
  return <hanko-auth/>;
}
```

Now simply import the component you just created.

```jsx pages/loginPage.tsx theme={null}
import HankoAuth from "../components/HankoAuth";

export default function LoginPage() {
  return (
      <HankoAuth/>
  );
}
```

### Hanko Profile

After setting up the HankoAuth let's set up the `HankoProfile.jsx` file to create an interface where users can
manage their `Email Addresses` and credentials.

For more information please refer to the [Profile component page.](/guides/hanko-elements/profile-component)

```jsx components/HankoProfile.tsx theme={null}
import { useEffect } from "react";
import { register } from "@teamhanko/hanko-elements";

const hankoApi = import.meta.env.VITE_HANKO_API_URL;

export default function HankoProfile() {
  useEffect(() => {
    register(hankoApi).catch((error) => {
      // handle error
      console.log(error)
    });
  }, []);

  return <hanko-profile />;
}

```

After you created the `HankoProfile` component, simply import it into any page.

```jsx pages/dashboardPage.tsx theme={null}
import HankoProfile from "../components/HankoProfile";

export default function Dashboard() {
  return (
    <div>
      <HankoProfile/>
    </div>
  );
}
```

## Setup your routes

After you created the `LoginPage.tsx` and `DashboardPage.tsx` you are able to import them into your react `App.tsx`.
We will use `react-router-dom` to setup the routes of your app.

```jsx App.tsx theme={null}
import './App.css'
import { Route, createBrowserRouter, createRoutesFromElements, RouterProvider } from 'react-router-dom';

import LoginPage from './pages/loginPage';
import Dashboard from './pages/dashboardPage';


const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/">
      {/* index route with Login page*/}
      <Route index element={<LoginPage />} /> 
       {/* dashboard route with Dashboard page*/}
      <Route path="dashboard" element={<Dashboard />} />
    </Route>
  )
)

export default function App(){
  return (
    <RouterProvider router={router} />
  )
}
```

By now you should be able to go to `/` to see the `<HankoAuth>`, and to `/dashboard` to see the `<HankoProfile>`.

They should look something like this👇

<Frame>
  <div style={{justifyContent: 'center', alignItems: 'center', display: 'flex', backgroundColor: 'white'}}>
    <img src="https://mintcdn.com/hanko/nDll7gWf2olRVk-k/images/fullstack-guide/next-one.png?fit=max&auto=format&n=nDll7gWf2olRVk-k&q=85&s=cfa08b5e9e30da72781967f53165a313" alt="sign up" width="500" data-path="images/fullstack-guide/next-one.png" />

    <img src="https://mintcdn.com/hanko/nDll7gWf2olRVk-k/images/fullstack-guide/next-two.png?fit=max&auto=format&n=nDll7gWf2olRVk-k&q=85&s=ddd4d20a613d80fcb7004d677fee3ed9" alt="profile page" width="500" data-path="images/fullstack-guide/next-two.png" />
  </div>
</Frame>

## Implement logout functionality

You can use `@teamhanko/hanko-elements` to easily logout users. Here we will make a logout button.
Create `LogoutButton.tsx` and insert the code below.

```jsx components/LogoutButton.tsx theme={null}
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { Hanko } from "@teamhanko/hanko-elements";

const hankoApi = import.meta.env.VITE_HANKO_API_URL;

function LogoutBtn() {
  const navigate = useNavigate();
  const [hanko, setHanko] = useState<Hanko>();

  useEffect(() => {
    import("@teamhanko/hanko-elements").then(({ Hanko }) =>
      setHanko(new Hanko(hankoApi ?? ""))
    );
  }, []);

  const logout = async () => {
    try {
      await hanko?.logout();

      navigate("/"); //Path to naviage to once the user logs out.

    } catch (error) {
      console.error("Error during logout:", error);
    }
  };

  return <button onClick={logout}>Logout</button>;
}

export default LogoutBtn;
```

## Customize component styles

You can customize the appearance of `hanko-auth` and `hanko-profile` components using CSS variables and parts. Refer to our [customization guide](/guides/hanko-elements/customize-appearance).

## Securing routes

To secure our routes we should validate the session token at the backend. Please refer to our [backend guides](/quickstarts/backend).

Lets set up a Private route to do this for us.
Create a new React component at `components/PrivateRoute.tsx`.

If the backend couldn't validate our token we get navigated back to `/`.
Otherwise if the validation was successfull it will return the children inside the private route.

```jsx components/PrivateRoute.tsx theme={null}
import { ReactNode, useEffect, useState } from 'react';
import { Navigate, useLocation } from 'react-router-dom';

export default function PrivateRoute({ children }: { children: ReactNode }) {
    const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
    const location = useLocation();
  
    useEffect(() => {
      fetch('http://localhost:5001/validate', {// Change this to the validation url of your running backend
        credentials: 'include', // This is required to include the cookie in the request
      })
        .then((res) => {
          setIsAuthenticated(res.ok);
        })
        .catch(() => {
          setIsAuthenticated(false);
        });
    }, []);
  
    if (isAuthenticated === null) {
      return null; // Or a loading spinner
    }

    if(isAuthenticated){ return <>{children}</> }

    //Url to naviage user to if they arent authenticated
    return <Navigate to="/" replace state={{ from: location }} />
  }
```

Lets import this `PrivateRoute.tsx` to your `App.tsx` file.\
To use the private route wrap your `Dashboard` in the Private Route;

```jsx App.tsx theme={null}
import './App.css'
import { Route, createBrowserRouter, createRoutesFromElements, RouterProvider } from 'react-router-dom';

import LoginPage from './pages/loginPage';
import Dashboard from './pages/dashboardPage';

import PrivateRoute from './components/PrivateRoute';

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/">
      {/* index route with Login page*/}
      <Route index element={ <LoginPage />} /> 
       {/* secured dashboard route with Dashboard page*/}
      <Route path="dashboard" element={<PrivateRoute> <Dashboard /> </PrivateRoute>} />
    </Route>
  )
)

export default function App(){
  return (
    <RouterProvider router={router} />
  )
}
```

To verify that it works, logout on your app and go to `/dashboard`, you should get redirected back.

## Getting user data

### Client side

Lets use the Hanko SDK to get user data.

Lets update the `dashboard` page to log some of the information from the user.

```jsx pages/dashboardPage.tsx theme={null}
import { useState, useEffect, useMemo } from "react";
import { Hanko } from "@teamhanko/hanko-elements";

import HankoProfile from "../components/HankoProfile";

const hankoApi = process.env.REACT_APP_HANKO_API_URL;

export default function Dashboard() {
  const hanko = useMemo(() => new Hanko(hankoApi), []);

  useEffect(() => setHanko(new Hanko(hankoApi)), []);
    useEffect(() => {
      hanko?.getUser()
        .then((user) => {
          console.log("User profile:", user);// Log user Profile
          console.log(user.emails?.[0]?.address);
          console.log(user.user_id);
        })
    }, [hanko]);

  return (
    <div>
      <HankoProfile/>
    </div>
  );
}
```

## Try it yourself

<Card
  title="React example (Vite)"
  href="https://github.com/teamhanko/hanko-react-express-starter"
  icon={
  <svg
    xmlns="http://www.w3.org/2000/svg"
    className="icon icon-tabler icon-tabler-external-link"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    strokeWidth="2"
    stroke="#5465FF"
    fill="none"
    strokeLinecap="round"
    strokeLinejoin="round"
  >
    <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
    <path d="M12 6h-6a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-6"></path>
    <path d="M11 13l9 -9"></path>
    <path d="M15 4h5v5"></path>
  </svg>
}
>
  It uses Express.js for the backend, full source code available on our GitHub.
</Card>
