When users don't have a Local First Auth mobile app installed (like Antler), this package gives them options to either create a one-time account or download the app.
This allows you to skip building user management and authentication systems. Users with a Local First Auth app can login with their existing profile, while users without one can still access your mini-app through the one-time account option.
- Dual onboarding paths: Download app or create web account
- DID-based authentication: Uses W3C Decentralized Identifiers (did:key)
- Local First Auth API compatible: Generates profiles that work identically to Antler
- Zero configuration: Works out-of-the-box with sensible defaults
- Customizable styling: Match your mini-app's branding
- Tiny bundle: Minimal dependencies
- Framework agnostic: Vanilla JS core + React bindings
npm install local-first-authimport { Onboarding } from 'local-first-auth/react'
function App() {
const hasLocalFirstAuth = typeof window !== 'undefined' && window.localFirstAuth
const [showOnboarding, setShowOnboarding] = useState(!hasLocalFirstAuth)
if (showOnboarding) {
return (
<Onboarding
mode="choice" // Shows both download and create account options
onComplete={(profile) => {
console.log('Profile created:', profile)
setShowOnboarding(false)
}}
customStyles={{ primaryColor: '#403B51' }}
/>
)
}
return <YourMiniApp />
}import { createOnboarding } from 'local-first-auth'
const onboarding = createOnboarding({
container: '#onboarding-root',
mode: 'choice',
onComplete: (profile) => {
console.log('Profile created:', profile)
// window.localFirstAuth is now available
}
})Shows both options: download Antler app or create one-time account.
<Onboarding mode="choice" />Only shows download buttons for iOS and Android.
<Onboarding mode="download-prompt" /><Onboarding
skipSocialStep={true} // Skip social links step
skipAvatarStep={true} // Skip avatar upload step
/><Onboarding
customStyles={{
primaryColor: '#403B51',
backgroundColor: '#ffffff',
textColor: '#333333',
borderRadius: '12px',
fontFamily: 'Inter, sans-serif',
inputRadius: '8px',
buttonRadius: '12px'
}}
/>- Name Step: User enters their name (required)
- Socials Step: Add social media links (optional)
- Instagram, X, Bluesky, LinkedIn, GitHub, and 15+ more
- Automatic validation and normalization
- URL preview
- Avatar Step: Upload and crop profile picture (optional)
- Automatic resize to 512x512px
- JPEG compression (~1MB max)
- Browser-based processing (no server required)
When a user creates a one-time account:
- DID Generation: Generates an Ed25519 keypair and did:key identifier
- Profile Storage: Saves profile data to LocalStorage
- API Injection: Injects
window.localFirstAuthobject - JWT Signing: All API methods return signed JWTs (compatible with Local First Auth spec)
The generated profile works identically to a profile from a Local First Auth mobile app. You can use this package to create a one-time account for users who do not have a Local First Auth mobile app installed and do not want to download one. Your backend can verify JWTs the same way it would for a profile from a Local First Auth mobile app.
Main wrapper component.
interface OnboardingProps {
mode?: 'download-prompt' | 'choice'
skipSocialStep?: boolean
skipAvatarStep?: boolean
customStyles?: CustomStyles
onComplete?: (profile: Profile) => void
}Shows iOS/Android download buttons.
interface DownloadPromptProps {
title?: string
description?: string
customStyles?: CustomStyles
}The 3-step account creation flow.
interface CreateAccountFlowProps {
skipSocialStep?: boolean
skipAvatarStep?: boolean
onComplete?: (profile: Profile) => void
customStyles?: CustomStyles
}Hook for detecting Local First Auth status and determining whether to show onboarding.
import { useOnboarding } from 'local-first-auth/react'
const { shouldShowOnboarding, profile, isLoading } = useOnboarding()
// Returns:
// - shouldShowOnboarding: boolean (true if no API available)
// - profile: Profile | null (user's profile if web account exists)
// - isLoading: boolean (initial loading state)
// Derived values you can compute:
// - hasApi = !shouldShowOnboarding
// - isNativeApp = !shouldShowOnboarding && profile === null
// - hasWebAccount = profile !== nullHook for accessing the current user profile.
import { useProfile } from 'local-first-auth/react'
const profile = useProfile()
// Returns Profile | null// Profile management
import {
createProfile,
getCurrentProfile,
updateProfile,
hasProfile,
clearProfile
} from 'local-first-auth'
// Device detection
import {
isLocalFirstAuth
} from 'local-first-auth'
// Social validation
import {
validateHandle,
normalizeHandle,
createSocialLink
} from 'local-first-auth'Profile data is stored in LocalStorage:
{
'local-first-auth:profile': {
did: 'did:key:z6Mk...',
name: 'Alice Anderson',
socials: [{platform: 'INSTAGRAM', handle: 'alice'}],
avatar: 'data:image/jpeg;base64,...'
},
'local-first-auth:privateKey': 'base64-encoded-64-byte-key'
}After profile creation, window.localFirstAuth is injected with these methods:
interface LocalFirstAuth {
getProfileDetails(): Promise<string> // Returns signed JWT
getAvatar(): Promise<string | null> // Returns signed JWT with avatar
getAppDetails(): AppDetails
requestPermission(permission: string): Promise<boolean>
close(): void
}All methods are compatible with the Local First Auth Specification. Users can generate a one-time account and your backend can verify JWTs that are generated by this package the same way it would for a profile from a Local First Auth mobile app.
This package includes a comprehensive example app in /example that demonstrates all features. The example app is excluded from the npm package (via "files": ["dist"] in package.json).
Run the example:
npm run dev:example
# Opens http://localhost:5173What it tests:
- Basic Demo:
useOnboarding()anduseProfile()hooks, state detection, native API simulation - Full Flow Demo: Complete onboarding flow with both modes (choice, download-prompt)
- Core API Demo: Vanilla JS testing of crypto, storage, profile, validation, and mock API injection
- Custom Style Demo: Custom theming with
customStylesprop
Build example:
cd example
npm install
npm run buildThe example app uses Vite for fast development with hot module replacement and demonstrates both React components and vanilla JS core functionality.
Full TypeScript support with comprehensive type definitions:
import type {
Profile,
SocialLink,
SocialPlatform,
LocalFirstAuth,
CustomStyles
} from 'local-first-auth'If you're migrating from irl-browser-onboarding v1:
npm uninstall irl-browser-onboarding
npm install local-first-auth// Before
import { IrlOnboarding, useIrlOnboarding } from 'irl-browser-onboarding/react'
// After
import { Onboarding, useOnboarding } from 'local-first-auth/react'window.irlBrowser→window.localFirstAuthgetBrowserDetails()→getAppDetails()isIRLBrowser()→isLocalFirstAuth()
Existing user profiles are automatically migrated from the old storage keys to the new ones on first load.
