This is a template for creating applications using Vite 6, HeroUI (v2) and a flexible authentication layer that supports multiple OAuth providers (Auth0, Dex, and more).
If you appreciate my work, please consider giving it a star! 🤩
Ths plugin uses our @sctg/vite-plugin-github-pages-spa Vite 6 plugin for handling the Github Pages limitations with SPA.
- 🚀 Fast development with Vite 6
- 🎨 Beautiful UI components from HeroUI v2
- 🔐 Flexible authentication with multiple OAuth providers (Auth0, Dex)
- 🌐 Internationalization with i18next (6 languages included)
- 🌙 Dark/Light mode support
- 📱 Responsive design
- 🍪 Cookie consent management
- 🧩 Type-safe with TypeScript
- 🧹 Code quality with ESLint 9
- 📦 Optimized build with manual chunk splitting
- Vite 6
- HeroUI
- Tailwind CSS 4
- Tailwind Variants
- React 19
- i18next
- Auth0 React SDK
- OIDC Client TS (For Dex and other OAuth providers)
- ESLint 9
- TypeScript
- Framer Motion
# Clone the repository
git clone https://github.com/sctg-development/vite-react-heroui-auth0-template.git
# Change directory
cd vite-react-heroui-auth0-template/client
# Install dependencies
npm install && cd ../cloudflare-fake-secured-api && npm install
# Create a .env file with your Auth0 credentials
cat <<EOF > .env
AUTH0_CLIENT_ID=your-auth0-client-id
AUTH0_CLIENT_SECRET=your-auth0-client-secret
AUTH0_DOMAIN=your-auth0-domain
AUTH0_SCOPE="openid profile email read:api write:api"
AUTH0_AUDIENCE=http://localhost:5173
API_BASE_URL=http://localhost:8787/api
CORS_ORIGIN=http://localhost:5173
READ_PERMISSION=read:api
EOF
# Start the development server
cd client && npm run dev:env &
# Start the Cloudflare Worker
cd cloudflare-fake-secured-api && npm run wrangler:env
- Vite, OAuth & HeroUI Template
- Star the project
- Live demo
- On Github Pages ?
- Features
- Technologies Used
- Quick Start
- Table of Contents
- Authentication
- Internationalization
- Cookie Consent
- Project Structure
- Available Scripts in the frontend application
- Deployment
- Tailwind CSS 4
- How to Use
- Contributing
- License
- Authentication Architecture
This template provides a flexible authentication system with support for multiple OAuth providers. The architecture uses an abstraction layer that allows you to easily switch between different providers while maintaining a consistent API. Currently, the template supports:
- Auth0 (Default) - Using the Auth0 React SDK
- Dex - Using the OIDC Client TS library
The authentication system can be extended to support other OAuth providers like Azure AD, Okta, or any OAuth 2.0 compliant service by implementing the provider interface.
-
Create an Auth0 Account:
- Go to Auth0 and sign up for a free account.
-
Create a New Application:
- In the Auth0 dashboard, navigate to the "Applications" section.
- Click on "Create Application".
- Choose a name for your application.
- Select "Single Page Web Applications" as the application type.
- Click "Create".
-
Configure Application Settings:
- In the application settings, you will find your
Client ID
andDomain
. - Set the "Allowed Callback URLs" to
http://localhost:5173
(or your development URL). - Set the "Allowed Logout URLs" to
http://localhost:5173
(or your development URL). - Set the "Allowed Web Origins" to
http://localhost:5173
(or your development URL).
- In the application settings, you will find your
-
Sample settings:
- The settings used by the demo deployment on GitHub Pages are:
- Allowed Callback URLs:
https://sctg-development.github.io/vite-react-heroui-auth0-template
- Allowed Logout URLs:
https://sctg-development.github.io/vite-react-heroui-auth0-template
- Allowed Web Origins:
https://sctg-development.github.io
- On Github repository settings, the
AUTH0_CLIENT_ID
secret is set to the Auth0 client ID and theAUTH0_DOMAIN
secret is set to the Auth0 domain. - The full Auth0 configuration screenshot is available here.
- Allowed Callback URLs:
- The settings used by the demo deployment on GitHub Pages are:
To keep your Auth0 credentials secure, use environment variables. Create a .env
file in the root of your project and add the following:
AUTH0_CLIENT_ID=your-auth0-client-id
AUTH0_CLIENT_SECRET=your-auth0-secret
AUTH0_DOMAIN=your-auth0-domain
AUTH0_SCOPE="openid profile email read:api write:api"
AUTH0_AUDIENCE=https://myapi.example.com
API_BASE_URL=https://myapi.example.com/api
CORS_ORIGIN=https://your-github-username.github.io
READ_PERMISSION=read:api
For using the provided GitHub Actions workflows, you need to add the following secrets to your repository:
AUTH0_CLIENT_ID=your-auth0-client-id
AUTH0_CLIENT_SECRET=your-auth0-secret
AUTH0_DOMAIN=your-auth0-domain
AUTH0_SCOPE="openid profile email read:api write:api"
AUTH0_AUDIENCE=https://myapi.example.com
API_BASE_URL=https://myapi.example.com/api
CORS_ORIGIN=https://your-github-username.github.io
READ_PERMISSION=read:api
each secrets should be manually entered in Github like:
You can use the AuthenticationGuard
component to protect routes that require authentication. This component works with any configured provider and will redirect users to the login page if they are not authenticated.
import { AuthenticationGuard } from "./authentication";
<Route
element={<AuthenticationGuard component={DocsPage} />}
path="/docs"
/>
The template includes a fully-configured secure API call system that demonstrates how to communicate with protected backend services using Auth0 token authentication.
To enable secure API calls in your application:
-
Create an API in Auth0 Dashboard:
- Navigate to "APIs" section in the Auth0 dashboard
- Click "Create API"
- Provide a descriptive name (e.g., "My Application API")
- Set the identifier (audience) - typically a URL or URI (e.g.,
https://api.myapp.com
) - Configure the signing algorithm (RS256 recommended)
-
Configure API Settings:
- Enable RBAC (Role-Based Access Control) if you need granular permission management
- Define permissions (scopes) that represent specific actions (e.g.,
read:api
,write:api
) - Configure token settings as needed (expiration, etc.)
- Include permissions in the access token
-
Set Environment Variables: Add the following to your
.env
file:AUTH0_AUDIENCE=your-api-identifier AUTH0_SCOPE="openid profile email read:api write:api" API_BASE_URL=http://your-api-url.com
-
Sample Configuration: For reference, view the Auth0 API configuration used in the demo deployment.
The template provides a hook useSecuredApi
that handles token acquisition and authenticated requests for any configured provider:
import { useSecuredApi } from "@/authentication";
// Inside your component:
const { getJson, postJson, deleteJson } = useSecuredApi();
// GET request to a secured API endpoint
const apiData = await getJson(`${import.meta.env.API_BASE_URL}/endpoint`);
// POST request to a secured API endpoint
const apiData = await postJson(`${import.meta.env.API_BASE_URL}/endpoint`, { data: "example" });
// DELETE request to a secured API endpoint
const apiData = await deleteJson(`${import.meta.env.API_BASE_URL}/endpoint`);
This function automatically:
- Requests the appropriate token with configured audience and scope
- Attaches the token to the request header
- Handles errors appropriately
- Returns the JSON response
For more control, you can use the authentication provider API directly:
import { useAuth } from "@/authentication";
// Inside your component:
const auth = useAuth();
// Access authentication state
const isLoggedIn = auth.isAuthenticated;
const userData = auth.user;
// Perform authentication actions
await auth.login();
await auth.logout();
// Get tokens for API calls
const token = await auth.getAccessToken();
// Make API calls
const data = await auth.getJson(`${import.meta.env.API_BASE_URL}/endpoint`);
await auth.postJson(`${import.meta.env.API_BASE_URL}/endpoint`, { key: 'value' });
This function automatically:
- Requests the appropriate token with configured audience and scope
- Attaches the token to the request header
- Handles errors appropriately
- Returns the JSON response
You can check user permissions with any configured authentication provider:
import { useAuth } from "@/authentication";
// Inside your component:
const auth = useAuth();
const canReadData = await auth.hasPermission("read:api");
// Or using the useSecuredApi hook
import { useSecuredApi } from "@/authentication";
const { hasPermission } = useSecuredApi();
const canReadData = await hasPermission("read:api");
The permission system works across different providers, with each implementation handling the specific token format of that provider.
This template includes a AuthenticationGuardWithPermission
component that works with any configured provider and wraps a component to check if the user has the required permission:
import { AuthenticationGuardWithPermission } from "@/authentication";
<AuthenticationGuardWithPermission permission="read:api">
<ProtectedComponent />
</AuthenticationGuardWithPermission>
For demonstration purposes, the template includes a Cloudflare Worker that acts as a secured backend API:
- Deploy the Worker:
cd cloudflare-fake-secured-api
npm install
cd ..
npm run wrangler
- Test API Integration:
With both your application and the worker running, navigate to the
/api
route in your application to see the secure API call in action.
- Your application requests an access token from Auth0 with specific audience and scope
- Auth0 issues a JWT token containing the requested permissions
- Your application includes this token in the Authorization header
- The backend API validates the token using Auth0's public key
- If valid, the API processes the request according to the permissions in the token
This template uses i18next for internationalization. The configuration and available languages are defined in the src/i18n.ts
file.
To add a new language to the application, follow these steps:
-
Update the
availableLanguages
array:- Open the
src/i18n.ts
file. - Add a new object to the
availableLanguages
array with the following properties:code
: The ISO 639-1 language code (e.g., "en-US").nativeName
: The native name of the language (e.g., "English").isRTL
: Whether the language is right-to-left (e.g.,false
).
- Open the
-
Create a Translation File:
- In the
src/locales/base
directory, create a new JSON file named with the language code (e.g.,en-US.json
). - Add the translations for the new language in this file.
- In the
-
Update the Load Path:
- In the
src/i18n.ts
file, manually add a switch case to theloadPath
function to handle the new JSON file for the added language.
- In the
The LanguageSwitch
component allows users to switch between the available languages. It is defined in the src/components/language-switch.tsx
file.
- The component uses the i18n instance to change the language and update the document metadata.
- It automatically updates the document direction based on the language (left-to-right or right-to-left).
- The selected language is stored in
localStorage
to persist the user's preference.
To use the LanguageSwitch
component in your application, simply include it in your JSX:
<LanguageSwitch availableLanguages={[{ code: "en-US", nativeName: "English", isRTL: false, isDefault: true },{ code: "fr-FR", nativeName: "Français", isRTL: false }]} />
or more simply using the availableLanguages
array defined in the src/i18n.ts
file:
import { availableLanguages } from "@/i18n";
<LanguageSwitch availableLanguages={availableLanguages} />
This component will render a dropdown menu with the available languages, allowing users to switch languages easily.
The default configuration uses the i18next-http-backend
plugin for language lazy loading. This means that translations are loaded only when needed, improving the application's performance.
- Configuration:
src/i18n.ts
- Translations:
src/locales/base
- Language Switch:
src/components/language-switch.tsx
By following the steps above, you can easily add new languages and manage internationalization for your application.
This template includes a cookie consent management system to comply with privacy regulations like GDPR. The system displays a modal dialog asking users for consent to use cookies and stores their preference in the browser's localStorage.
- Modern modal-based UI with blur backdrop
- Internationalized content for all supported languages
- Stores user preferences in localStorage
- Provides a context API for checking consent status throughout the application
- Supports both accepting and rejecting cookies
The cookie consent feature can be enabled or disabled through the site configuration:
- Enable/Disable Cookie Consent:
- Open the
src/config/site.ts
file - Set the
needCookieConsent
property totrue
orfalse
:
- Open the
export const siteConfig = () => ({
needCookieConsent: true, // Set to false if you don't need cookie consent
// ...other configuration
});
- Context Provider:
src/contexts/cookie-consent-context.tsx
- Provides a React context to manage consent state - UI Component:
src/components/cookie-consent.tsx
- Renders the consent modal using HeroUI components - Consent Status: The consent status can be one of three values:
pending
: Initial state, user hasn't made a decision yetaccepted
: User has accepted cookiesrejected
: User has rejected cookies
You can access the cookie consent status in any component using the useCookieConsent
hook:
import { useCookieConsent } from "@/contexts/cookie-consent-context";
const MyComponent = () => {
const { cookieConsent, acceptCookies, rejectCookies, resetCookieConsent } = useCookieConsent();
// Load analytics only if cookies are accepted
useEffect(() => {
if (cookieConsent === "accepted") {
// Initialize analytics, tracking scripts, etc.
}
}, [cookieConsent]);
// ...rest of your component
};
- Modify the appearance of the consent modal in
src/components/cookie-consent.tsx
- Add custom tracking or cookie management logic in the
acceptCookies
andrejectCookies
functions insrc/contexts/cookie-consent-context.tsx
- Update the cookie policy text in the language files (e.g.,
src/locales/base/en-US.json
)
This template follows a mono-repository structure with the frontend application and Cloudflare Worker in separate directories.
vite-react-heroui-auth0-template/
├──client # Frontend application
│ ├──public/ # Static assets
│ ├──src/
│ ├── authentication/ # Authentication system
│ │ ├── auth-components.tsx # Authentication UI components
│ │ ├── auth-root.tsx # Root authentication provider
│ │ ├── index.ts # Exports
│ │ └── providers/ # Provider implementations
│ │ ├── auth-provider.ts # Provider interface
│ │ ├── auth0-provider.tsx # Auth0 implementation
│ │ ├── dex-provider.tsx # Dex implementation
│ │ └── use-auth.tsx # Auth context and hooks
│ ├── components/ # Reusable UI components
│ ├── config/ # Configuration files
│ ├── hooks/ # Custom React hooks
│ ├── layouts/ # Page layout components
│ ├── locales/ # Translation files
│ ├── pages/ # Page components
│ ├── styles/ # Global styles
│ ├── types/ # TypeScript definitions
│ ├── App.tsx # Main application component
│ ├── i18n.ts # i18next configuration
│ ├── main.tsx # Application entry point
│ └── provider.tsx # HeroUI provider setup
├── cloudflare-fake-secured-api/ # Cloudflare Worker for testing
├── .github/ # GitHub workflows and configuration
├── .vscode/ # VS Code configuration
├── tailwind.config.js # Tailwind CSS configuration
├── vite.config.ts # Vite configuration
└── update-heroui.ts # Helper script to update HeroUI packages
# Start the development server
npm run dev
# Start the development server with environment variables
npm run dev:env
# Run the Cloudflare Worker
npm run wrangler
# Build for production
npm run build
# Run ESLint to check and fix code
npm run lint
# Preview production build locally
npm run preview
# Update HeroUI packages to the latest beta
npm run update:heroui
This template includes a GitHub Actions workflow to automatically deploy your application to GitHub Pages. To use this feature:
- Enable GitHub Pages in the repository settings and set the source to GitHub Actions
- Enable GitHub Actions in the repository settings
- Add your Auth0 credentials as GitHub repository secrets:
AUTH0_CLIENT_ID
AUTH0_DOMAIN
- Push the changes to your repository
- The application will be deployed automatically on each push to the main branch
This template uses Tailwind CSS 4, which is a utility-first CSS framework. You can customize the styles by modifying the tailwind.config.js
file.
Currently HeroUI uses Tailwind CSS 3, but @winchesHe create a port of HeroUI to Tailwind CSS 4, you can find it here, HeroUI packages are available at heroui-inc/heroui#4656 (comment).
To clone the project, run the following command:
git clone https://github.com/sctg-development/vite-react-heroui-auth0-template.git
In the vite.config.ts
file, all @heroui
packages are manually split into a separate chunk. This is done to reduce the size of the main bundle. You can remove this configuration if you don't want to split the packages.
You can use one of them npm
, yarn
, pnpm
, bun
, Example using npm
:
npm install
npm run dev
npm run wrangler
If you are using pnpm
, you need to add the following code to your .npmrc
file:
public-hoist-pattern[]=*@heroui/*
After modifying the .npmrc
file, you need to run pnpm install
again to ensure that the dependencies are installed correctly.
Contributions are welcome! Please feel free to submit a Pull Request.
This template is primarily licensed under the MIT license.
Exception: Four specific files (site-loading.tsx
, language-switch.tsx
, vite.config.ts
, and auth0.tsx
) are licensed under the AGPL-3.0 license as they contain code originating from my other repositories.
The authentication system uses a provider-based architecture that allows you to easily switch between different OAuth providers:
All authentication providers implement a common interface that defines standard authentication methods:
export interface AuthProvider {
// Authentication state
isAuthenticated: boolean;
isLoading: boolean;
user: AuthUser | null;
// Core authentication methods
login(options?: LoginOptions): Promise<void>;
logout(options?: LogoutOptions): Promise<void>;
getAccessToken(options?: TokenOptions): Promise<string | null>;
// Permission handling
hasPermission(permission: string): Promise<boolean>;
// API interaction helpers
getJson(url: string): Promise<any>;
postJson(url: string, data: any): Promise<any>;
deleteJson(url: string): Promise<any>;
}
To use the authentication system in your application, wrap your components with the AuthenticationProvider
:
import { AuthenticationProvider } from "./authentication";
// For Auth0 (default)
<AuthenticationProvider providerType="auth0">
<App />
</AuthenticationProvider>
// For Dex
<AuthenticationProvider
providerType="dex"
>
<App />
</AuthenticationProvider>
To use Auth0, follow these steps:
-
Create an Auth0 Account:
- Go to Auth0 and sign up for a free account.
-
Create a New Application:
- In the Auth0 dashboard, navigate to the "Applications" section.
- Click on "Create Application".
- Choose a name for your application.
- Select "Single Page Web Applications" as the application type.
- Click "Create".
-
Configure Application Settings:
- In the application settings, you will find your
Client ID
andDomain
. - Set the "Allowed Callback URLs" to
http://localhost:5173
(or your development URL). - Set the "Allowed Logout URLs" to
http://localhost:5173
(or your development URL). - Set the "Allowed Web Origins" to
http://localhost:5173
(or your development URL).
- In the application settings, you will find your
-
Sample settings:
- The settings used by the demo deployment on GitHub Pages are:
- Allowed Callback URLs:
https://sctg-development.github.io/vite-react-heroui-auth0-template
- Allowed Logout URLs:
https://sctg-development.github.io/vite-react-heroui-auth0-template
- Allowed Web Origins:
https://sctg-development.github.io
- On Github repository settings, the
AUTH0_CLIENT_ID
secret is set to the Auth0 client ID and theAUTH0_DOMAIN
secret is set to the Auth0 domain. - The full Auth0 configuration screenshot is available here.
- Allowed Callback URLs:
- The settings used by the demo deployment on GitHub Pages are:
-
Configure API in Auth0:
- Navigate to "APIs" section in the Auth0 dashboard
- Click "Create API"
- Provide a descriptive name (e.g., "My Application API")
- Set the identifier (audience) - typically a URL or URI (e.g.,
https://api.myapp.com
) - Configure the signing algorithm (RS256 recommended)
-
Configure API Settings:
- Enable RBAC (Role-Based Access Control) if you need granular permission management
- Define permissions (scopes) that represent specific actions (e.g.,
read:api
,write:api
) - Configure token settings as needed (expiration, etc.)
- Include permissions in the access token
-
Set Environment Variables: Add the following to your
.env
file:AUTHENTICATION_PROVIDER_TYPE=auth0 AUTH0_AUDIENCE=your-api-identifier AUTH0_SCOPE="openid profile email read:api write:api" API_BASE_URL=http://your-api-url.com
-
Sample Configuration: For reference, view the Auth0 API configuration used in the demo deployment.
Dex is an identity service that uses OpenID Connect to drive authentication for other apps. To use Dex as your authentication provider:
-
Setup a Dex Server:
- Install and configure a Dex server following the official documentation
- Configure Dex to support the OAuth 2.0 authorization code flow
-
Register your Application in Dex:
- Add your application to the Dex configuration
- Set the redirect URI to your application's callback URL (e.g.,
http://localhost:5173
)
-
Configure the Dex Provider:
- Create a
.env
file with your Dex configuration:
AUTHENTICATION_PROVIDER_TYPE=dex DEX_AUTHORITY=https://your-dex-server.com DEX_CLIENT_ID=your-dex-client-id DEX_SCOPE="openid profile email" DEX_AUDIENCE=https://your-api.com DEX_JWKS_ENDPOINT=https://your-dex-server.com/dex/keys
- Create a
-
Initialize the Dex Provider:
import { AuthenticationProvider } from "./authentication"; <AuthenticationProvider providerType="dex" > <App /> </AuthenticationProvider>
To add support for additional OAuth providers:
- Create a new provider implementation file in
src/authentication/providers/
- Implement the
AuthProvider
interface - Add the new provider to the
AuthProviderWrapper
insrc/authentication/providers/use-auth.tsx
- Add configuration in
src/authentication/auth-root.tsx
The modular design makes it easy to extend the authentication system with new providers while maintaining a consistent API throughout your application.