@clowk/react
v1.0.2
Published
React components and hooks for Clowk authentication
Readme
@clowk/react
React components and hooks for Clowk authentication. Handles the redirect-based auth flow — no embedded UI, no iframes.
Install
npm install @clowk/reactArchitecture
Clowk is an authentication broker. The React package provides:
<ClowkProvider>— wraps your app, captures the?token=callback, manages auth state- Redirect buttons —
<SignInButton>and<SignUpButton>redirect to your Clowk instance - Hooks —
useAuth(),useClowk(),useToken()for accessing auth state <SignOutButton>— clears auth state and cookie
User clicks <SignInButton>
↓
Browser redirects to https://{subdomain}.clowk.dev/sign-in
↓
User authenticates (Google, GitHub, email, etc.)
↓
Clowk redirects back to your app with ?token=eyJ...
↓
<ClowkProvider> captures token, verifies it, provides user via contextQuick Start
import { ClowkProvider, SignInButton, SignOutButton, useAuth } from '@clowk/react'
function App() {
return (
<ClowkProvider publishableKey="pk_live_...">
<AuthContent />
</ClowkProvider>
)
}
function AuthContent() {
const { user, signedIn, isLoading } = useAuth()
if (isLoading) return <p>Loading...</p>
if (!signedIn) {
return (
<div>
<h1>Welcome</h1>
<SignInButton>Sign in with Clowk</SignInButton>
</div>
)
}
return (
<div>
<h1>Hello, {user?.email}</h1>
<p>User ID: {user?.sub}</p>
<SignOutButton>Log out</SignOutButton>
</div>
)
}Components
<ClowkProvider>
Wraps your app and manages the authentication state. Must be at the top of your component tree.
<ClowkProvider
publishableKey="pk_live_..." // identifies your Clowk instance
secretKey="sk_live_..." // optional, for client-side JWT verification
tokenParam="token" // query param to read (default: "token")
afterSignOutPath="/" // redirect after sign out (default: "/")
>
{children}
</ClowkProvider>What it does on mount:
- Checks the URL for
?token=... - If found, removes it from the URL (no page reload)
- Decodes the JWT payload (verifies if
secretKeyis provided) - Makes the user available via context
<SignInButton>
Redirects to your Clowk instance's sign-in page.
// Default text
<SignInButton />
// Custom text
<SignInButton>Log in with SSO</SignInButton>
// Custom redirect URI
<SignInButton redirectUri="https://myapp.com/auth/callback" />
// Override publishable key
<SignInButton publishableKey="pk_live_..." />
// HTML button props are forwarded
<SignInButton className="btn btn-primary" id="login-btn" />The button is disabled until the Clowk subdomain URL is resolved.
<SignUpButton>
Same API as <SignInButton>, redirects to the sign-up page.
<SignUpButton>Create an account</SignUpButton>
<SignUpButton className="btn-secondary" redirectUri="/auth/callback" /><SignOutButton>
Clears auth state and cookie. Only renders when the user is signed in.
<SignOutButton />
<SignOutButton>Log out</SignOutButton>
<SignOutButton className="text-red-500" />Hooks
useAuth()
Returns the current authentication state. Must be used inside <ClowkProvider>.
import { useAuth } from '@clowk/react'
function Profile() {
const { user, token, signedIn, isLoading, signOut } = useAuth()
if (isLoading) return <Spinner />
if (!signedIn) return <p>Not signed in</p>
return (
<div>
<img src={user?.avatar_url} />
<h2>{user?.name}</h2>
<p>{user?.email}</p>
<button onClick={signOut}>Sign out</button>
</div>
)
}Return type:
| Property | Type | Description |
|---|---|---|
| user | JwtPayload \| null | Decoded JWT payload |
| token | string \| null | Raw JWT string |
| signedIn | boolean | true if user is present |
| isLoading | boolean | true during initial token extraction |
| signOut | () => void | Clears state and cookie |
useClowk()
Returns a ClowkClient instance for API calls. Does not require <ClowkProvider>.
import { useClowk } from '@clowk/react'
function UserList() {
const client = useClowk()
const [users, setUsers] = useState([])
useEffect(() => {
client.users.list().then(res => setUsers(res.bodyParsed.data))
}, [client])
return <ul>{users.map(u => <li key={u.id}>{u.email}</li>)}</ul>
}Pass options to override config:
const client = useClowk({ apiBaseUrl: 'https://custom.api.dev/v1' })useToken()
Returns the raw JWT string. Must be used inside <ClowkProvider>.
import { useToken } from '@clowk/react'
function ApiCall() {
const token = useToken()
const fetchData = async () => {
const res = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` },
})
// ...
}
}Protecting routes
Combine useAuth() with your router to protect pages:
import { useAuth } from '@clowk/react'
import { Navigate } from 'react-router-dom'
function ProtectedRoute({ children }) {
const { signedIn, isLoading } = useAuth()
if (isLoading) return <Spinner />
if (!signedIn) return <Navigate to="/login" />
return children
}
// Usage
<Route path="/dashboard" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />Full example with React Router
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { ClowkProvider, SignInButton, SignOutButton, useAuth } from '@clowk/react'
function App() {
return (
<BrowserRouter>
<ClowkProvider publishableKey={import.meta.env.VITE_CLOWK_PK}>
<Nav />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</ClowkProvider>
</BrowserRouter>
)
}
function Nav() {
const { signedIn } = useAuth()
return (
<nav>
<a href="/">Home</a>
{signedIn ? <SignOutButton /> : <SignInButton />}
</nav>
)
}
function Home() {
return <h1>Welcome to MyApp</h1>
}
function Dashboard() {
const { user, signedIn, isLoading } = useAuth()
if (isLoading) return <p>Loading...</p>
if (!signedIn) return <p>Please sign in</p>
return <h1>Dashboard for {user?.email}</h1>
}