spry-apps-dropdown
v3.0.4
Published
React components for Spry apps dropdown and profile menu with dynamic API integration - Google-style UI
Downloads
1,209
Maintainers
Readme
spry-apps-dropdown
A React component library for Spry apps dropdown and profile menu with multi-account switching. Features a Google-style UI with beautiful animations and automatic Keycloak authentication.
Features
- 🎨 Beautiful Material-UI design with smooth animations (Google-inspired)
- 👥 Multi-account switching - Users can log into multiple accounts and switch between them
- 🔄 Automatic data fetching from API with caching
- 🔐 Built-in Keycloak/OIDC authentication via react-oidc-context
- ⚡ Loading and error states
- 📱 Responsive layout
- 🎯 TypeScript support
- 🔌 Easy integration - Just 3 steps!
Installation
npm install spry-apps-dropdown react-oidc-context oidc-client-tsPeer Dependencies:
react^18.0.0 || ^19.0.0react-dom^18.0.0 || ^19.0.0react-oidc-context^3.3.0oidc-client-ts^1.0.0
Quick Start (3 Steps)
Step 1: Wrap Your App with SpryAuthProvider
// main.tsx or index.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { SpryAuthProvider } from 'spry-apps-dropdown'
import App from './App'
const oidcConfig = {
authority: 'https://auth.sprylogin.com/realms/sprylogin',
client_id: 'your-client-id',
redirect_uri: window.location.origin,
post_logout_redirect_uri: window.location.origin,
onSigninCallback: () => {
window.history.replaceState({}, document.title, window.location.pathname)
},
}
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<SpryAuthProvider config={oidcConfig}>
<App />
</SpryAuthProvider>
</React.StrictMode>
)Step 2: Use useSpryAccountManager Hook
// App.tsx
import { useSpryAccountManager } from 'spry-apps-dropdown'
function App() {
const accountManager = useSpryAccountManager()
return (
<div>
{/* Your app content */}
</div>
)
}Step 3: Add the TopBar Component
// App.tsx
import { TopBar, useSpryAuth, useSpryAccountManager } from 'spry-apps-dropdown'
function App() {
const auth = useSpryAuth()
const accountManager = useSpryAccountManager()
return (
<div>
<AppBar>
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
My App
</Typography>
<TopBar
apiUrl="https://sprylogin.com/apps-api"
onSignOut={() => auth.signoutRedirect()}
getAuthToken={() => auth.user?.access_token || null}
accountManager={accountManager}
/>
</Toolbar>
</AppBar>
{/* Your app content */}
</div>
)
}That's it! Your app now has:
- ✅ Multi-account switching
- ✅ Apps dropdown
- ✅ Profile menu
- ✅ Automatic authentication
Using Auth Token in Your API Calls
Get the current user's token using useSpryAuth():
import { useSpryAuth } from 'spry-apps-dropdown'
function MyComponent() {
const auth = useSpryAuth()
useEffect(() => {
const token = auth.user?.access_token
fetch('/api/data', {
headers: {
'Authorization': `Bearer ${token}`
}
})
}, [auth.user])
}For React Query:
import { useSpryAuth } from 'spry-apps-dropdown'
import { useQuery } from '@tanstack/react-query'
function MyComponent() {
const auth = useSpryAuth()
const { data } = useQuery({
queryKey: ['data'],
queryFn: async () => {
const token = auth.user?.access_token
const res = await fetch('/api/data', {
headers: { 'Authorization': `Bearer ${token}` }
})
return res.json()
},
enabled: !!auth.user,
})
}Multi-Account Switching
The package automatically handles multi-account switching:
- User clicks "Add Another Account" in ProfileMenu
- Redirects to Keycloak for login (with
prompt=login) - New account is added to localStorage
- User can switch between accounts from ProfileMenu
- API calls automatically use the active account's token
No manual code needed - it just works!
API Reference
TopBar Component
Combined apps dropdown + profile menu (recommended).
Props
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| apiUrl | string | Yes | - | Base URL for apps API |
| accountManager | UseAccountManagerReturn | Yes | - | Account manager from useSpryAccountManager() |
| profileApiUrl | string | No | Same as apiUrl | Base URL for profile API |
| onSignOut | () => void | No | - | Callback when user signs out |
| getAuthToken | () => string \| null | No | - | Function to get auth token |
| profileHeaders | Record<string, string> | No | - | Custom headers for profile API |
| showAppsDropdown | boolean | No | true | Show apps dropdown |
| showProfileMenu | boolean | No | true | Show profile menu |
| appsRefetchInterval | number | No | 300000 (5 min) | Apps refetch interval (ms) |
| appsCacheTime | number | No | 300000 (5 min) | Apps cache duration (ms) |
| profileRefetchInterval | number | No | 300000 (5 min) | Profile refetch interval (ms) |
| profileCacheTime | number | No | 300000 (5 min) | Profile cache duration (ms) |
SpryAuthProvider Component
Wraps your app with Keycloak/OIDC authentication.
Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| config | UserManagerSettings | Yes | OIDC configuration object |
| children | ReactNode | Yes | Your app components |
Example config:
const oidcConfig = {
authority: 'https://auth.sprylogin.com/realms/sprylogin',
client_id: 'my-app-client',
redirect_uri: window.location.origin,
post_logout_redirect_uri: window.location.origin,
onSigninCallback: () => {
window.history.replaceState({}, document.title, window.location.pathname)
},
}useSpryAccountManager() Hook
Returns account manager for multi-account switching.
Returns
{
accounts: StoredAccount[] // All logged-in accounts
activeAccount: StoredAccount // Currently active account
switchAccount: (id: string) => Promise<void>
addNewAccount: () => Promise<void>
removeAccount: (id: string) => Promise<void>
refreshAccountToken: (id: string) => Promise<void>
}useSpryAuth() Hook
Returns the current authentication state (alias for useAuth() from react-oidc-context).
Returns
{
user: User | null // Current user object
isAuthenticated: boolean // Is user logged in
isLoading: boolean // Is auth loading
signinRedirect: () => Promise<void>
signoutRedirect: () => Promise<void>
// ... other react-oidc-context methods
}Advanced: Handling Account Switches
If you're using React Query and want to refetch data when accounts switch:
import { useSpryAccountManager } from 'spry-apps-dropdown'
import { useQueryClient } from '@tanstack/react-query'
import { useEffect } from 'react'
function useMultiAccountManager() {
const queryClient = useQueryClient()
const accountManager = useSpryAccountManager()
// Listen for account switches and invalidate queries
useEffect(() => {
const handleAccountSwitch = () => {
queryClient.invalidateQueries() // Refetch all data
}
window.addEventListener('auth:account-switched', handleAccountSwitch)
return () => window.removeEventListener('auth:account-switched', handleAccountSwitch)
}, [queryClient])
return accountManager
}Backend API Requirements
Your backend should expose these endpoints:
GET /api/apps
Returns list of apps for the dropdown.
{
"apps": [
{
"id": "abc123",
"name": "SpryBoard",
"description": "Collaborative whiteboard",
"url": "https://board.spry.com",
"iconUrl": "https://example.com/icon.png",
"color": "#4285f4",
"order": 1,
"createdAt": "2026-02-13T10:00:00.000Z",
"updatedAt": "2026-02-13T10:00:00.000Z"
}
],
"lastUpdated": "2026-02-13T10:00:00.000Z"
}GET /api/profile
Returns user profile information (requires Bearer token).
GET /api/profile
Authorization: Bearer <jwt-token>Response:
{
"email": "[email protected]",
"firstName": "John",
"lastName": "Doe",
"avatar": "https://example.com/avatar.jpg",
"manageAccountUrl": "https://account.spry.com"
}Customization
Custom Theme
import { ThemeProvider, createTheme } from '@mui/material/styles'
const theme = createTheme({
palette: {
primary: {
main: '#1a73e8',
},
},
})
<ThemeProvider theme={theme}>
<SpryAuthProvider config={oidcConfig}>
<App />
</SpryAuthProvider>
</ThemeProvider>Hide Apps Dropdown or Profile Menu
<TopBar
apiUrl="https://sprylogin.com/apps-api"
showAppsDropdown={false} // Hide apps dropdown
showProfileMenu={true} // Show only profile menu
accountManager={accountManager}
/>Troubleshooting
"User not authenticated" error
Make sure you wrapped your app with SpryAuthProvider:
<SpryAuthProvider config={oidcConfig}>
<App />
</SpryAuthProvider>Account switching doesn't work
Pass the accountManager prop to TopBar:
const accountManager = useSpryAccountManager()
<TopBar accountManager={accountManager} {...otherProps} />API calls use wrong token after switching
Use useSpryAuth() to get the current token:
const auth = useSpryAuth()
const token = auth.user?.access_tokenTypeScript Support
Full TypeScript support included. Types are automatically available:
import type {
UseAccountManagerReturn,
StoredAccount,
OIDCUser,
TopBarProps
} from 'spry-apps-dropdown'Browser Support
- Modern browsers with ES2020 support
- Requires localStorage API
License
MIT
Related Documentation
- INTEGRATION.md - Detailed multi-account integration guide
- Spry Apps Server - Backend API server
Support
For issues or questions:
- Open an issue on GitHub
- Check INTEGRATION.md for detailed guides
