The Guides System provides a composable hook architecture that integrates directly into the SammyAgentProvider for seamless walkthrough experiences.
Performance Update : Guides now use lazy loading to reduce API calls. User guides are only fetched when components call refreshUserGuides(), not automatically on initialization. This prevents unnecessary API load from users who never interact with guides. Always call refreshUserGuides() in components that display guides.
Architecture Overview
Clean Architecture The guides system uses a hook-based composition pattern that integrates directly into the main provider:
No nested contexts - Integrated directly into main provider
Hook composition - Clean separation of concerns
Zero overhead - Returns null when disabled
Type-safe - Full TypeScript support
Data Flow
Quick Start
Basic Setup
Provider Configuration
Using Guides in Components
import { SammyAgentProvider } from '@sammy-labs/sammy-three' ;
function App () {
return (
< SammyAgentProvider
config = { {
auth: authConfig ,
captureMethod: 'render' ,
debugLogs: true ,
model: 'models/gemini-2.5-flash-preview-native-audio-dialog' ,
} }
guides = { true } // Enable guides functionality
autoStartFromURL = { true } // Auto-start from ?walkthrough= URLs
onError = { ( error ) => console . error ( 'Agent error:' , error ) }
onTokenExpired = { handleTokenExpired }
onWalkthroughStart = { ( guideId ) => {
console . log ( 'Starting walkthrough:' , guideId );
} }
>
< YourApp />
</ SammyAgentProvider >
);
}
URL-Based Activation
Guides can be automatically triggered via URL parameters, perfect for sharing specific walkthroughs or onboarding flows:
# Trigger a guide directly from URL
https://yourapp.com?walkthrough =onboarding-guide-v1
# Custom parameter name
https://yourapp.com?guide =quick-tour
When autoStartFromURL is enabled, the guide will automatically start when the page loads with the appropriate query parameter.
Guide Lifecycle
Manage the complete lifecycle of guides from initialization to completion:
Initialization
// Guides automatically initialize when provider mounts
// Fetches user guides and checks for URL parameters
Manual Start
// Start a guide programmatically
const success = await guides . startWalkthrough ( 'guide-id' );
if ( success ) {
console . log ( 'Guide started successfully' );
}
URL Detection
// Check for guide in URL
const hasGuide = await guides . checkForGuideInUrl ();
if ( hasGuide ) {
console . log ( 'Guide found in URL and started' );
}
Progress Tracking
// Guides are automatically marked as completed when conversation starts
// Manual completion is also possible
guides . markGuideAsCompleted ( 'guide-id' );
Data Refresh
// Refresh guide data at any time
guides . refreshGuide (); // Current guide
guides . refreshUserGuides (); // All user guides
Cleanup
// Clean up URL params after use
guides . cleanupUrlParams ();
Core Components
GuidesService
The service layer handles all API communication and transforms snake_case responses to camelCase.
export interface GuidesService {
fetchGuide : ( guideId : string ) => Promise < SammyGuide | null >;
getUserGuides : () => Promise < SammyGuide []>;
}
export interface SammyGuide {
guideId : string ;
title : string ;
prompt : string ;
organisationId : string ;
isCompleted : boolean ;
}
export const fetchGuide = ( apiCaller : SammyApiClient ) =>
async ( guideId : string ) : Promise < SammyGuide | null > => {
try {
const response = await apiCaller . get < KeysToSnakeCase < SammyGuide >>(
`/api/v1/sammy-three/guides/ ${ guideId } `
);
return {
guideId: response . guide_id ,
title: response . title ,
prompt: response . prompt ,
organisationId: response . organisation_id ,
isCompleted: response . is_completed ,
};
} catch ( error ) {
console . error ( '[GuidesService] Failed to validate guide:' , error );
return null ;
}
};
useGuides Hook
Provides always-on guide data with automatic fetching:
Return Interface
Hook Usage
interface UseGuidesReturn {
// Data
currentGuide : SammyGuide | null ;
userGuides : SammyGuide [];
// Loading states
isLoadingGuide : boolean ;
isLoadingUserGuides : boolean ;
// Error states
guideError : Error | null ;
userGuidesError : Error | null ;
// Actions
refreshGuide : () => void ;
refreshUserGuides : () => void ;
markGuideAsCompleted : ( guideId : string ) => void ;
}
Features
URL-Based Walkthrough Activation
User Visits URL
User visits: https://app.com?walkthrough=guide-123
Parameter Detection
GuidesProvider detects the parameter
Guide Fetching
Guide is fetched automatically
Auto-Start
If autoStartFromURL=true, agent starts with guide context
URL Cleanup
URL parameter is cleaned up
Completion Tracking
Guide marked as completed on conversation start
Guide Discovery & Listing
List All Guides
Guide Selector Component
const { guides } = useSammyAgentContext ();
// List all guides with completion status
guides ?. userGuides . map ( guide => ({
id: guide . guideId ,
title: guide . title ,
completed: guide . isCompleted
}));
Manual Walkthrough Triggering
guides ?. startWalkthrough ( 'guide-123' );
// This will:
// 1. Call onWalkthroughStart callback
// 2. Start agent with guide context
// 3. Mark guide as completed
Progress Tracking
Automatic Completion Guides automatically track completion:
Marked complete when conversation starts with guide
Optimistic UI updates (immediate visual feedback)
Persistent across sessions (backend storage)
Query Parameter Detection
Configuration Options
Basic Setup
Configuration Table
< SammyAgentProvider
config = { authConfig }
guides = { true } // Enable guides
autoStartFromURL = { true } // Auto-start from URL
guidesQueryParam = "walkthrough" // Query param name (default)
guidesDebug = { true } // Enable debug logging
>
< YourApp />
</ SammyAgentProvider >
Prop Type Default Description guidesbooleanfalseEnable guides functionality autoStartFromURLbooleanfalseAuto-start agent when guide detected in URL guidesQueryParamstring"walkthrough"Name of the query parameter to detect guidesDebugbooleanfalseEnable debug console logging
Usage Examples
Share Walkthrough function ShareWalkthroughButton ({ guideId }) {
const shareUrl =
` ${ window . location . origin } ?walkthrough= ${ guideId } ` ;
return (
< button onClick = { () =>
navigator . clipboard . writeText ( shareUrl )
} >
Copy Walkthrough Link
</ button >
);
}
Email Campaign < a href = "https://app.com?walkthrough=new-user-onboarding" >
Start Your Guided Tour
</ a >
Programmatic Navigation function navigateWithWalkthrough ( guideId : string ) {
window . location . href =
`/dashboard?walkthrough= ${ guideId } ` ;
}
Manual URL Check const { guides } = useSammyAgentContext ();
const handleCheckUrl = async () => {
if ( guides ) {
const found = await guides . checkForGuideInUrl ();
if ( found ) {
console . log ( 'Guide found and started!' );
}
}
};
Advanced Features
< SammyAgentProvider
guides = { true }
guidesQueryParam = "tutorial" // Now use ?tutorial=guide-id
>
function App () {
const [ userReady , setUserReady ] = useState ( false );
return (
< SammyAgentProvider
guides = { true }
autoStartFromURL = { userReady } // Only auto-start when ready
>
< YourApp />
</ SammyAgentProvider >
);
}
Enable detailed console logging: < SammyAgentProvider
guides = { true }
guidesDebug = { true } // Logs all detection and cleanup
>
Example debug output: [useGuidesQueryParams] Guide detected in URL: onboarding-guide
[useGuidesQueryParams] Starting guide: onboarding-guide
[GuidesProvider] Guide detected from URL: onboarding-guide
[GuidesProvider] 🚀 Auto-starting walkthrough: onboarding-guide
[useGuidesQueryParams] Guide started successfully: onboarding-guide
[useGuidesQueryParams] Cleaned up 'walkthrough' parameter from URL
Implementation Details
Conditional Provider Wrapping
The system uses a clever pattern to conditionally wrap providers without nesting:
// When guides={true}, wrap with GuidesProvider
if ( guides ) {
return (
< GuidesProvider >
< InternalProvider >
{ children }
</ InternalProvider >
</ GuidesProvider >
);
}
// When guides={false}, skip the wrapper
return < InternalProvider >{ children } </ InternalProvider > ;
Dependency Injection for Agent Start
// Extract startAgent function from inner context
const StartAgentExtractor = ({ onStartAgentReady }) => {
const context = useContext ( SammyAgentContext );
useEffect (() => {
if ( context ?. startAgent ) {
onStartAgentReady ( context . startAgent );
}
}, [ context ?. startAgent , onStartAgentReady ]);
return null ;
};
Optimistic Updates
Immediate UI Feedback Guide completion is updated optimistically for better UX: const markGuideAsCompleted = useCallback (( guideId : string ) => {
// Update current guide immediately
if ( currentGuide ?. guideId === guideId ) {
setCurrentGuide ( prev =>
prev ? { ... prev , isCompleted: true } : null
);
}
// Update user guides list immediately
setUserGuides ( prev =>
prev . map ( guide =>
guide . guideId === guideId
? { ... guide , isCompleted: true }
: guide
)
);
}, [ currentGuide ]);
API Reference
Provider Props
interface SammyAgentProviderProps {
guides ?: boolean ; // Enable guides functionality
autoStartFromURL ?: boolean ; // Auto-start from URL params
onWalkthroughStart ?: ( guideId : string ) => void ;
// ... other props
}
Context API
const { guides } = useSammyAgentContext ();
if ( guides ) {
// All guides functionality available
guides . currentGuide ; // Current active guide
guides . userGuides ; // All user's guides
guides . isLoadingGuide ; // Loading state
guides . guideError ; // Error state
guides . refreshGuide (); // Refresh current
guides . refreshUserGuides (); // Refresh list
guides . markGuideAsCompleted ( id ); // Mark complete
guides . startWalkthrough ( id ); // Start walkthrough
}
Service Endpoints
Single Guide
User Guides List
GET /api/v1/sammy-three/guides/{id}
Complete Example
Provider Setup
Dashboard Component
import { SammyAgentProvider , useSammyAgentContext } from '@sammy-three/react' ;
function App () {
return (
< SammyAgentProvider
config = { {
auth: { token: 'xxx' , baseUrl: 'https://api.sammy.ai' },
model: 'gemini-2.0-flash-exp' ,
} }
guides = { true }
autoStartFromURL = { true }
guidesQueryParam = "walkthrough"
guidesDebug = { process . env . NODE_ENV === 'development' }
onWalkthroughStart = { ( guideId ) => {
console . log ( `Walkthrough started: ${ guideId } ` );
// Track analytics event
analytics . track ( 'walkthrough_started' , { guideId });
} }
>
< Dashboard />
</ SammyAgentProvider >
);
}
function Dashboard () {
const { guides } = useSammyAgentContext ();
// Share current page with a walkthrough
const shareWithGuide = ( guideId : string ) => {
const url = new URL ( window . location . href );
url . searchParams . set ( 'walkthrough' , guideId );
navigator . clipboard . writeText ( url . toString ());
};
return (
< div >
{ guides ?. currentGuide && (
< div > Active Guide: { guides . currentGuide . title } </ div >
) }
< button onClick = { () => shareWithGuide ( 'dashboard-tour' ) } >
Share Dashboard Tour
</ button >
</ div >
);
}
Zero-Cost When Disabled When guides={false}:
No GuidesProvider rendered
No API calls made
No state management overhead
No event listeners attached
Memoization All expensive operations are memoized: const sammyApiClient = useMemo (() =>
authConfig ? new SammyApiClient ( authConfig ) : null ,
[ authConfig ]
);
Automatic Cleanup Resources are properly cleaned up: // URL parameter cleaned after detection
window . history . replaceState ({}, '' , newUrl . toString ());
TypeScript Best Practices Use null checks for type safety: // Good - null safe
if ( guides ) {
guides . userGuides . map ( ... );
}
// Bad - assumes guides exists
guides ! . userGuides . map ( ... );
Security Considerations
Always validate guide IDs on the backend:
Check if guide exists
Verify user permissions
Validate organization access
URL Parameter Sanitization
The system automatically:
Sanitizes guide IDs before use
Prevents XSS through parameter injection
Cleans up parameters after processing
Troubleshooting
Common Issues
Guide Not Starting
URL Not Cleaning Up
Multiple Triggers
Troubleshooting Steps:
Check if guides={true} is set
Verify autoStartFromURL={true} for automatic start
Ensure guide ID exists and user has access
Enable debug mode to see detailed logs
Troubleshooting Steps:
Check browser console for errors
Ensure window.history.replaceState is available
Verify no other code is modifying the URL
Troubleshooting Steps:
Parameters are cleaned after first detection
Check for duplicate provider instances
Ensure checkQueryOnMount isn’t called multiple times
Summary
The Sammy-Three Guides System provides a robust, performant, and developer-friendly solution for implementing guided experiences. Its clean architecture, type safety, and zero-overhead design make it an excellent choice for applications requiring tutorial or walkthrough functionality.
Key takeaways:
Clean Architecture : Separated concerns with proper layering
Performance First : Zero cost when disabled
Developer Experience : Full TypeScript support and simple API
User Experience : Seamless URL-based activation and progress tracking
Extensible : Easy to add new features without breaking existing functionality