Create a React application
To setup our React Frontend we will use vite as a build tool.
Run the following command to create a new Vite React application:
npm create vite@latest project-name -- --template react-ts
Install @teamhanko/hanko-elements
Once you’ve initialized your React app, installing hanko-elements provides you with access to the prebuilt components: hanko-auth
and hanko-profile
.
We will also install the react-router-dom
, this will allow us to navigate trough pages / routes.
cd project-name
npm install @teamhanko/hanko-elements react-router-dom
Setup your Hanko project
Go to the Hanko console and create a project for this application.
During creation make sure to input the URL you will be developing on as the APP URL
.
(Most likely http://localhost:5173/)
Add the Hanko API URL
Retrieve the API URL from the Hanko console and place it in your .env file.
VITE_HANKO_API_URL=https://f4****-4802-49ad-8e0b-3d3****ab32.hanko.io
If you are self-hosting you need to provide the URL of your running Hanko
backend.
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.
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, 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.
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.
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.
components/HankoProfile.tsx
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.
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.
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👇
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.
components/LogoutButton.tsx
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.
Securing routes
To secure our routes we should validate the session token at the backend. Please refer to our backend guides.
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.
components/PrivateRoute.tsx
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;
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.
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
React example (Vite)
It uses Express.js for the backend, full source code available on our GitHub.