logto-authkit supports custom navigation to integrate seamlessly with your routing library, preventing full page reloads during authentication flows.
Why Custom Navigation?
By default, Logto uses window.location.href for navigation, which causes full page reloads. Custom navigation allows you to:
Use client-side routing (React Router, Next.js, etc.)
Maintain application state during auth flows
Provide a smoother user experience
Control navigation behavior in SPAs
Basic Setup
Pass a customNavigate function to the AuthProvider:
import { AuthProvider } from '@ouim/logto-authkit'
const config = {
endpoint: 'https://your-logto-endpoint.com' ,
appId: 'your-app-id' ,
}
function App () {
const customNavigate = ( url , options ) => {
// Your navigation logic here
console . log ( 'Navigating to:' , url , options )
}
return (
< AuthProvider
config = { config }
callbackUrl = "http://localhost:3000/callback"
customNavigate = { customNavigate }
>
< YourApp />
</ AuthProvider >
)
}
React Router Integration
React Router v6
import { AuthProvider } from '@ouim/logto-authkit'
import { BrowserRouter , useNavigate } from 'react-router-dom'
function AppProviders ({ children }) {
const navigate = useNavigate ()
const customNavigate = ( url , options ) => {
// Handle both relative and absolute URLs
if ( url . startsWith ( 'http://' ) || url . startsWith ( 'https://' )) {
// External URL - use window.location
window . location . href = url
} else {
// Internal URL - use React Router
navigate ( url , { replace: options ?. replace })
}
}
return (
< AuthProvider
config = { logtoConfig }
callbackUrl = "/callback"
customNavigate = { customNavigate }
>
{ children }
</ AuthProvider >
)
}
function App () {
return (
< BrowserRouter >
< AppProviders >
< YourApp />
</ AppProviders >
</ BrowserRouter >
)
}
React Router v5
import { AuthProvider } from '@ouim/logto-authkit'
import { BrowserRouter , useHistory } from 'react-router-dom'
function AppProviders ({ children }) {
const history = useHistory ()
const customNavigate = ( url , options ) => {
if ( url . startsWith ( 'http://' ) || url . startsWith ( 'https://' )) {
window . location . href = url
} else {
if ( options ?. replace ) {
history . replace ( url )
} else {
history . push ( url )
}
}
}
return (
< AuthProvider
config = { logtoConfig }
callbackUrl = "/callback"
customNavigate = { customNavigate }
>
{ children }
</ AuthProvider >
)
}
Next.js Integration
App Router (Next.js 13+)
'use client'
import { AuthProvider } from '@ouim/logto-authkit'
import { useRouter } from 'next/navigation'
function Providers ({ children }) {
const router = useRouter ()
const customNavigate = ( url , options ) => {
if ( url . startsWith ( 'http://' ) || url . startsWith ( 'https://' )) {
window . location . href = url
} else {
if ( options ?. replace ) {
router . replace ( url )
} else {
router . push ( url )
}
}
}
return (
< AuthProvider
config = { logtoConfig }
callbackUrl = "/callback"
customNavigate = { customNavigate }
>
{ children }
</ AuthProvider >
)
}
Pages Router (Next.js 12 and earlier)
import { AuthProvider } from '@ouim/logto-authkit'
import { useRouter } from 'next/router'
function MyApp ({ Component , pageProps }) {
const router = useRouter ()
const customNavigate = ( url , options ) => {
if ( url . startsWith ( 'http://' ) || url . startsWith ( 'https://' )) {
window . location . href = url
} else {
if ( options ?. replace ) {
router . replace ( url )
} else {
router . push ( url )
}
}
}
return (
< AuthProvider
config = { logtoConfig }
callbackUrl = "/callback"
customNavigate = { customNavigate }
>
< Component { ... pageProps } />
</ AuthProvider >
)
}
Navigation Options
The customNavigate function receives two parameters:
The URL to navigate to (can be relative or absolute)
Navigation options object: Whether to replace the current history entry instead of pushing a new one
How It Works
Provider Registration
When you pass customNavigate to AuthProvider, it registers your navigation function globally for the library.
Internal Navigation Calls
When logto-authkit needs to navigate (e.g., after sign-in, during callback), it uses your custom function instead of window.location.href.
Cleanup on Unmount
When the AuthProvider unmounts, it automatically unregisters the custom navigation function.
Implementation Details
The custom navigation is set using the setCustomNavigate utility from the library:
// From context.tsx (line 360-365)
useEffect (() => {
setCustomNavigate ( customNavigate || null )
// Cleanup on unmount
return () => setCustomNavigate ( null )
}, [ customNavigate ])
This ensures that:
The navigation function is available throughout the library
It’s properly cleaned up when the component unmounts
You can dynamically change the navigation function if needed
Best Practices
Always check if the URL is external (starts with http:// or https://) and use window.location.href for those cases. Authentication flows may redirect to external Logto servers. const customNavigate = ( url , options ) => {
if ( url . startsWith ( 'http://' ) || url . startsWith ( 'https://' )) {
window . location . href = url
} else {
// Use your router
}
}
Respect the options.replace parameter to allow the library to replace history entries when appropriate. const customNavigate = ( url , options ) => {
if ( options ?. replace ) {
router . replace ( url )
} else {
router . push ( url )
}
}
Make sure your customNavigate function is stable (use useCallback if needed) to prevent unnecessary re-renders. const customNavigate = useCallback (( url , options ) => {
// navigation logic
}, [ router ])
Custom navigation works seamlessly with popup sign-in mode:
import { AuthProvider } from '@ouim/logto-authkit'
import { useNavigate } from 'react-router-dom'
function AppProviders ({ children }) {
const navigate = useNavigate ()
const customNavigate = ( url , options ) => {
if ( url . startsWith ( 'http://' ) || url . startsWith ( 'https://' )) {
window . location . href = url
} else {
navigate ( url , { replace: options ?. replace })
}
}
return (
< AuthProvider
config = { logtoConfig }
callbackUrl = "/callback"
customNavigate = { customNavigate }
enablePopupSignIn = { true } // Popup mode
>
{ children }
</ AuthProvider >
)
}
When using popup sign-in, the main window navigation is handled by your custom function, while the popup window uses its own navigation context.
Troubleshooting
Full page reload after sign-in
Make sure your customNavigate function is properly handling relative URLs. Check that you’re not falling back to window.location.href for internal routes.
Verify that your router hook is available in the component where you define customNavigate. For React Router, ensure you’re inside a <BrowserRouter>. For Next.js, make sure you’re in a client component ('use client').
Check that your callback URL matches the route where you render <CallbackPage />. Mismatched URLs can cause redirect loops.
Next Steps
AuthProvider Learn more about AuthProvider configuration
useAuth Hook Learn more about the useAuth hook