User authentication is an integral part of any application—whether a web app, mobile app, or an entire organization's infrastructure. It ensures that only authorized users can access specific resources, data, or services. It helps to prevent data breaches, unauthorized access, and other security threats.
Authentication in React, while straightforward for experienced developers, can be complicated for beginners. This also depends on the features the developers want to include in their authentication, like passwordless login (Google, Facebook, Apple, etc.), multi-factor authentication, and user roles. The more features included, the more detailed it becomes. That is where Clerk comes in, providing a simple solution without the hassle of building from scratch.
Clerk is a user management platform that simplifies the authentication process and offers everything needed for user authentication and management, including features like user profiles, sessions, passwordless login, Multi-factor authentication, Email and SMS OTP and many more.
Clerk also supports a variety of frameworks (Frontend and Backend) and integrations like NextJs, React, Node/Express, Ruby on Rails, Firebase, Supabase, Hasura, Google Analytics and others.
In this tutorial, we will build a simple authentication app using React. And we'll style it using Tailwindcss.
Beginner developers should learn to build user authentication from scratch to understand the process and how to implement it effectively.
Before starting this tutorial, you should have a good understanding of the fundamentals of JavaScript and React. Additionally, you must have the following software installed on your computer:
npm create vite@latest clerkapp
.
clerkapp
. Open this directory in your preferred text editor.
npm install
.
npm run dev
.
http://localhost:5173/
in your browser to access the Vite welcome page.npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
src/index.css
, remove the boilerplate designs and add:@tailwind base;
@tailwind components;
@tailwind utilities;
App.jsx
, and remove the boilerplate code and replace it with:import React from 'react';
const App = () => {
return (
<div className="min-h-screen bg-purple-300 flex items-center justify-center">
<h1 className="text-indigo-400 font-bold">Hello, TailwindCSS!</h1>
</div>
);
};
export default App;
Restart the server, and see the new changes.
Note: Sometimes, Tailwindcss may not take effect immediately. In such a case, you can either uninstall and then reinstall Tailwindcss or regenerate the config files: npx tailwindcss init -p
npm install @clerk/clerk-react.
Create a .env.local
file in your root folder to set up your environment variables, and add your API key. It should look like:
VITE_CLERK_PUBLISHABLE_KEY=pk_test_w29vZmJjYXNpbmctY29ucmF0ZS0xMy5jbGVyay5hY2NvdW50cy5kZXYkRg
This is a fake publishable key.
main.jsx
file to import your Clerk publishable key, and wrap your app with Clerk's ClerkProvider
component to enable authentication features throughout the app.import React from 'react';
import ReactDOM from 'react-dom/client'
import App from './App';
import './index.css';
import { ClerkProvider } from '@clerk/clerk-react';
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
if (!PUBLISHABLE_KEY) {
throw new Error('Clerk publishable key is missing.');
}
ReactDOM.createRoot(document.getElementById('root')).render(
<ClerkProvider publishableKey={PUBLISHABLE_KEY}>
<App />
</ClerkProvider>
);
In this step, we'll create the app navigation components: the Header, the Layout, the Homepage and the Dashboard.
npm install react-router-dom
src
folder, create a new components
folder and cd
into it. This is where we'll store all of our authentication components and layouts.
Header.jsx
.Add the following code:import React from 'react';
import { Link } from 'react-router-dom';
import { useUser, useClerk } from '@clerk/clerk-react';
const Header = () => {
const { user } = useUser();
const { signOut } = useClerk();
return (
<nav className="flex items-center justify-between px-6 py-4 mb-5 bg-black">
<div className="flex items-center">
<Link to="/">
<div className="text-lg font-bold text-purple-300 uppercase">
ClerkApp
</div>
</Link>
</div>
<div className="flex items-center text-purple-200">
{!user && (
<>
<Link
to="/sign-in"
className="text-purple-300 hover:text-purple-400 mr-4"
>
Sign In
</Link>
<Link
to="/sign-up"
className="text-purple-300 hover:text-purple-400 mr-4"
>
Sign Up
</Link>
</>
)}
{user && (
<>
<Link
to="/profile"
className="text-purple-300 hover:text-purple-400 mr-4"
>
Profile
</Link>
<button
onClick={() => signOut()}
className="text-purple-300 hover:text-purple-400"
>
Sign Out
</button>
</>
)}
</div>
</nav>
);
};
export default Header;
Dashboard.jsx
. This is the page users will see once signed in. It'll include a small welcome message with the username. We'll use the useUser
hook from the Clerk library to display the username.import React from 'react';
import { useUser } from '@clerk/clerk-react';
const Dashboard = () => {
const { user } = useUser();
return (
<div className="min-h-screen bg-purple-50 flex flex-col items-center justify-center">
<div className="bg-white shadow-lg rounded-xl p-10 w-full max-w-lg">
<h1 className="text-4xl font-extrabold text-purple-600 mb-6">Dashboard</h1>
<p className="text-lg text-gray-700 mb-6 font-bold text-purple-800 capitalize">
Hello {user ? user.username : user.fisrtName}!
</p>
<p> Welcome to Clerk App, where you can manage your User Authentication with ease.</p>
</div>
</div>
);
};
export default Dashboard;
Layout.jsx
. This is a wrapper component for the app to ensure the Header component is consistently displayed across various parts of the app. You are free to customize it to your needs.import React from 'react';
import Header from './Header';
const Layout = ({ children }) => (
<div className="layout-container">
<Header />
<main>{children}</main>
</div>
);
export default Layout;
HomePage.jsx
, this will be the app's landing page. This page can be accessed by both authenticated and unauthenticated users.import React from 'react';
const HomePage = () => {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-100 p-4">
<h1 className="text-4xl font-bold text-purple-600 mb-4">Welcome to Clerk App</h1>
<p className="text-lg text-gray-700 mb-2">This is a public homepage that anyone can view.</p>
<p className="text-md text-gray-600">Please sign in to access more features.</p>
</div>
);
};
export default HomePage;
In this step, we'll create all the components involved in user authentication. The Sign-in, the Sign-up, and Profile components.
SignIn.jsx
file for the sign-in component.import React from 'react';
import { SignIn } from '@clerk/clerk-react';
const SignInPage = () => (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<SignIn path="/sign-in" routing="path" />
</div>
</div>
);
export default SignInPage;
SignUp.jsx
file for the sign-up component.import React from 'react';
import { SignUp } from '@clerk/clerk-react';
const SignUpPage = () => (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<SignUp path="/sign-up" routing="path" />
</div>
</div>
);
export default SignUpPage;
Profile.jsx
file for the profile component.import React from 'react';
import { UserProfile } from '@clerk/clerk-react';
const Profile = () => {
return (
<div className="flex items-center justify-center min-h-screen">
<UserProfile path="/profile" routing="path" />
</div>
);
};
export default Profile;
App.jsx
, and import and render all the previously created components.import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Dashboard from './components/Dashboard';
import Profile from './components/Profile';
import SignInPage from './components/SignIn';
import SignUpPage from './components/SignUp';
import HomePage from './components/HomePage';
import Layout from './components/Layout';
import './index.css'
const App = () => (
<Router>
<Layout>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
<Route path="/sign-in" element={<SignInPage />} />
<Route path="/sign-up" element={<SignUpPage />} />
</Routes>
</Layout>
</Router>
);
export default App;
Restart your server, and look at the changes. You should now have a page with a welcome message and be able to sign up, sign in, sign out, or view your profile.
Now that the authentication is almost complete, we need to ensure that certain routes like Dashboard
are restricted to unauthenticated users. To do this, we'll use Clerk's SignedIn
, SignedOut
and RedirectToSignIn
components to handle this.
import { SignedIn, SignedOut, RedirectToSignIn } from '@clerk/clerk-react';
SignedIn
component.
SignedOut
component for public routes like the sign-in
and sign-out
routes which are accessible to unauthenticated users.
RedirectToSignIn
component.
App.jsx
file, your code should look like this:import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { SignedIn, SignedOut, RedirectToSignIn } from '@clerk/clerk-react';
import Dashboard from './components/Dashboard';
import Profile from './components/Profile';
import SignInPage from './components/SignIn';
import SignUpPage from './components/SignUp';
import HomePage from './components/HomePage';
import Layout from './components/Layout';
import './index.css';
const App = () => {
return (
<Router>
<Layout>
<Routes>
{/* HomePage Route */}
<Route
path="/"
element={<HomePage />}
/>
{/* Dashboard Route */}
<Route
path="/dashboard"
element={
<SignedIn>
<Dashboard />
</SignedIn>
}
/>
{/* Profile Route */}
<Route
path="/profile"
element={
<SignedIn>
<Profile />
</SignedIn>
}
/>
{/* Sign In Route */}
<Route
path="/sign-in"
element={
<SignedOut>
<SignInPage />
</SignedOut>
}
/>
{/* Sign Up Route */}
<Route
path="/sign-up"
element={
<SignedOut>
<SignUpPage />
</SignedOut>
}
/>
{/* Redirect unauthenticated to sign-in */}
<Route
path="*"
element={
<SignedOut>
<RedirectToSignIn />
</SignedOut>
}
/>
</Routes>
</Layout>
</Router>
);
};
export default App;
The SignedIn
component renders its children only if the user is signed In, the SignedOut
component renders its children when the user is unauthenticated and the RedirectToSignIn
component restricts unauthorized access.
For easy navigation, we'll make some slight changes in the Header.
We want authenticated users to be able to access their dashboard and homepage, so we will use conditional rendering to ensure that the Dashboard link is only visible when a user is signed in.
import React from 'react';
import { Link } from 'react-router-dom';
import { useUser, useClerk } from '@clerk/clerk-react';
const Header = () => {
const { user } = useUser();
const { signOut } = useClerk();
return (
<nav className="flex items-center justify-between px-6 py-4 mb-5 bg-black">
<div className="flex items-center">
<Link to="/">
<div className="text-lg font-bold text-purple-300 uppercase">
ClerkApp
</div>
</Link>
</div>
<div className="flex items-center text-purple-200">
{!user && (
<>
<Link
to="/sign-in"
className="text-purple-300 hover:text-purple-400 mr-4 uppercase"
>
Sign In
</Link>
<Link
to="/sign-up"
className="text-purple-300 hover:text-purple-400 mr-4 uppercase"
>
Sign Up
</Link>
</>
)}
{user && (
<>
<Link
to="/dashboard"
className="text-purple-300 hover:text-purple-400 mr-4"
>
Dashboard
</Link>
<Link
to="/profile"
className="text-purple-300 hover:text-purple-400 mr-4"
>
Profile
</Link>
<button
onClick={() => signOut()}
className="text-purple-300 hover:text-purple-400"
>
Sign Out
</button>
</>
)}
</div>
</nav>
);
};
export default Header;
You have successfully integrated Clerk and set up user authentication for your application. Restart your development server to review the changes. The app should be fully authenticated.
|
|
---|
In this tutorial, we used Clerk to add user authentication to our React app. We built an application where users can sign in, sign up, and access protected routes like the Dashboard and Profile. We also integrated TailwindCSS for styling. Additionally, we used conditional rendering in the Header component to improve navigation based on authentication status.
To expand your understanding of Clerk and TailwindCSS, refer to their official documentation for more features and customization options.