
Security News
Feross on the 10 Minutes or Less Podcast: Nobody Reads the Code
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.
@layers/foyer
Advanced tools
Foyer SDK for React Native/Expo - Dynamic onboarding flows with A/B testing
Foyer SDK for React Native and Expo - Dynamic onboarding flows with A/B testing.
npm install @layers/foyer
# or
yarn add @layers/foyer
First, create an API key in your Foyer dashboard at https://foyer.yourdomain.com/dashboard/api-keys
import { Foyer } from '@layers/foyer';
const foyer = new Foyer({
apiKey: process.env.FOYER_API_KEY, // Your Foyer API key
projectId: 'your-project-id',
enableAnalytics: true,
debug: __DEV__,
});
const flow = await foyer.startFlow('user-123', {
onAnswer: (data) => {
console.log('Screen answered:', data);
// Update your app state, save to AsyncStorage, etc.
},
onComplete: (data) => {
console.log('Flow completed!', data);
// Navigate to main app
navigation.navigate('Home');
},
onAbandon: (data) => {
console.log('Flow abandoned:', data);
},
onError: (error) => {
console.error('Error:', error);
},
});
import { FoyerFlow } from '@layers/foyer';
function OnboardingScreen() {
return (
<FoyerFlow
foyer={foyer}
userId="user-123"
callbacks={{
onComplete: (data) => {
navigation.navigate('Home');
},
}}
/>
);
}
Foyer Classnew Foyer(config: FoyerConfig)
Config Options:
apiKey (required) - Your Foyer API key (get from dashboard)projectId (required) - Your Foyer project IDenableAnalytics (optional) - Enable event tracking (default: true)enableOfflineQueue (optional) - Queue events offline (default: false)debug (optional) - Enable debug logging (default: false)startFlow(userId: string, callbacks?: FoyerCallbacks): Promise<Flow>Start a flow for a user. Returns the flow definition.
submitAnswer(answerValue: unknown): Promise<void>Submit an answer for the current screen and advance to the next.
skipScreen(): Promise<void>Skip the current screen (only if it's optional).
abandonFlow(): Promise<void>Abandon the current flow.
getCurrentScreen(): Screen | nullGet the current screen definition.
getCurrentScreenIndex(): numberGet the index of the current screen (0-based).
getTotalScreens(): numberGet the total number of screens in the flow.
hasNextScreen(): booleanCheck if there are more screens after the current one.
getProgress(): numberGet progress percentage (0-100).
flush(): Promise<void>Manually flush queued analytics events.
interface FoyerCallbacks {
onAnswer?: (data: AnswerCallbackData) => void;
onComplete?: (data: CompleteCallbackData) => void;
onAbandon?: (data: AbandonCallbackData) => void;
onError?: (error: Error) => void;
}
onAnswerFires when a user answers a screen.
{
screen_id: string;
screen_index: number;
screen_type: ScreenType;
answer_value: unknown;
flow_id: string;
variant_id: string;
}
onCompleteFires when the flow is completed.
{
flow_id: string;
variant_id: string;
total_time_ms: number;
screens_completed: number;
}
onAbandonFires when the flow is abandoned.
{
flow_id: string;
variant_id: string;
screen_id: string;
screen_index: number;
total_time_ms: number;
}
Display information with optional image.
{
"id": "welcome",
"type": "info",
"title": "Welcome!",
"body": "Let's get started",
"image": "https://example.com/image.png"
}
Single selection from options.
{
"id": "bedtime",
"type": "single_choice",
"question": "When do you go to bed?",
"options": ["Before 10PM", "10-12PM", "After midnight"]
}
Multiple selections from options.
{
"id": "goals",
"type": "multiple_choice",
"question": "What are your goals?",
"options": ["Sleep better", "Exercise more", "Eat healthier"],
"minSelections": 1,
"maxSelections": 3
}
Free-form text input.
{
"id": "name",
"type": "text_input",
"question": "What's your name?",
"placeholder": "Enter your name",
"maxLength": 50,
"validation": "none"
}
Validation options: "email", "phone", "url", "none"
Numeric range selection.
{
"id": "hours",
"type": "slider",
"question": "How many hours do you sleep?",
"min": 4,
"max": 12,
"step": 0.5,
"minLabel": "4h",
"maxLabel": "12h",
"defaultValue": 8
}
Date/time selection.
{
"id": "birthday",
"type": "date_input",
"question": "When is your birthday?",
"mode": "date"
}
Mode options: "date", "time", "datetime"
If you want to build custom UI instead of using the pre-built components:
import { Foyer } from '@layers/foyer';
function CustomOnboarding() {
const [foyer] = useState(() => new Foyer({ /* ... */ }));
const [screen, setScreen] = useState(null);
useEffect(() => {
async function start() {
await foyer.startFlow('user-123', {
onAnswer: (data) => {
// Handle answer
setScreen(foyer.getCurrentScreen());
},
onComplete: () => {
// Navigate away
},
});
setScreen(foyer.getCurrentScreen());
}
start();
}, []);
const handleAnswer = async (answer) => {
await foyer.submitAnswer(answer);
};
if (!screen) return <Loading />;
// Render custom UI based on screen.type
switch (screen.type) {
case 'info':
return <CustomInfoScreen screen={screen} onContinue={() => handleAnswer(null)} />;
case 'single_choice':
return <CustomChoiceScreen screen={screen} onAnswer={handleAnswer} />;
// ... etc
}
}
The SDK is written in TypeScript and includes full type definitions:
import type {
Flow,
Screen,
InfoScreen,
SingleChoiceScreen,
AnswerCallbackData,
// ... etc
} from '@layers/foyer';
All flows and events are validated at runtime using Zod:
import { validateFlow, isValidScreen } from '@layers/foyer';
try {
const flow = validateFlow(data);
} catch (error) {
console.error('Invalid flow:', error);
}
if (isValidScreen(data)) {
// Safe to use
}
Enable offline event queuing:
const foyer = new Foyer({
// ...
enableOfflineQueue: true,
});
// Events will be queued and sent when online
// Manually flush if needed
await foyer.flush();
Check the /examples directory for complete integration examples:
Proprietary - Not licensed for public use
FAQs
Foyer SDK for React Native/Expo - Dynamic onboarding flows with A/B testing
We found that @layers/foyer demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.

Research
/Security News
Campaign of 108 extensions harvests identities, steals sessions, and adds backdoors to browsers, all tied to the same C2 infrastructure.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.