Custom code lets you go beyond the visual editor’s capabilities. Calculate values
based on user input, integrate with your backend, or add tracking scripts - the
Fox API gives you programmatic control over your funnel.
Required element: All custom code must be added through the
Raw HTML element. This is the only way to add JavaScript
to your funnels.
Quick Start
Add a Raw HTML element to any screen and include your JavaScript in <script>
tags:
<script>
// Calculate BMI from user inputs
const weight = parseInt(fox.inputs.get('weight'));
const height = parseInt(fox.inputs.get('height'));
const bmi = weight / ((height/100) * (height/100));
// Store result as new variable
fox.inputs.set('bmi', bmi.toFixed(1));
// Now you can use {{bmi}} anywhere in your funnel
</script>
The Fox API is available globally as window.fox
(or just fox
) as soon as
the screen loads.
Common Use Cases
Transform and combine user responses to create new data:
<script>
// Get user inputs (using element IDs)
const age = parseInt(fox.inputs.get('age'));
const income = parseInt(fox.inputs.get('income'));
const familySize = parseInt(fox.inputs.get('family-size'));
// Calculate affordability score
const score = (income / familySize) - (age * 100);
// Save for use as {{affordability-score}}
fox.inputs.set('affordability-score', score);
// Navigate based on calculation
if (score > 5000) {
fox.navigation.goToId('premium-offer');
} else {
fox.navigation.goToId('standard-offer');
}
</script>
Custom Backend Integration
Handle authentication or validation with your own servers:
<script>
// Add to screen with email input and button with "Do Nothing" action
document.getElementById('register-button')
.children[0] // Access the actual button element
.addEventListener('click', async () => {
const email = fox.inputs.get('email');
const name = fox.inputs.get('name');
try {
const response = await fetch('https://your-api.com/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, name })
});
if (response.ok) {
const data = await response.json();
// Store user ID from your backend
fox.inputs.set('user-id', data.userId);
fox.navigation.goNext();
} else {
// Show error (make sure error-message element exists)
document.getElementById('error-message').style.display = 'block';
}
} catch (error) {
console.error('Registration failed:', error);
}
});
</script>
Dynamic Navigation Logic
Create complex flows based on multiple conditions:
<script>
const age = parseInt(fox.inputs.get('age'));
const hasKids = fox.inputs.get('has-kids') === 'yes';
const income = fox.inputs.get('income-range');
// Multi-condition routing
if (age < 25 && !hasKids) {
fox.navigation.goToId('young-singles');
} else if (age < 25 && hasKids) {
fox.navigation.goToId('young-families');
} else if (income === 'high' && age > 40) {
fox.navigation.goToId('premium-segment');
} else {
fox.navigation.goToId('standard-flow');
}
</script>
Third-Party Tracking
Add custom tracking beyond standard integrations:
<script>
// Custom analytics event
fox.trackCustom('QuizCompleted', {
score: fox.inputs.get('quiz-score'),
time_spent: Date.now() - startTime,
answers: fox.inputs.getAll()
});
// Third-party pixel
if (typeof fbq !== 'undefined') {
fbq('trackCustom', 'LeadQualified', {
value: fox.inputs.get('estimated-value'),
currency: 'USD'
});
}
// Google Tag Manager
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'funnel_milestone',
milestone: 'pricing_viewed',
user_segment: fox.inputs.get('segment')
});
</script>
Fox API Reference
Core Properties
Property | Type | Description |
---|
fox.sandbox | boolean | True in preview mode, false in production |
fox.locale | string | User’s browser language code (e.g., “en-US”) |
Navigation Methods
Control funnel flow programmatically:
// Navigate to next screen in sequence
fox.navigation.goNext();
// Go back to previous screen
fox.navigation.goBack();
// Jump to specific screen by ID
fox.navigation.goToId('checkout');
// Navigate by screen index (0-based)
fox.navigation.goToIndex(3);
Deprecated methods: Don’t use navigateToScreen()
, navigateToId()
,
navigateToNext()
, or navigateToBack()
. These legacy methods exist for
backwards compatibility only.
Read and write user data:
// Get input value by element ID
const email = fox.inputs.get('email');
const age = fox.inputs.get('age-select');
// Set custom input (creates {{variable}})
fox.inputs.set('calculated-price', 99.99);
fox.inputs.set('user-segment', 'premium');
// Get all inputs as object
const allData = fox.inputs.getAll();
// Returns: { email: "[email protected]", age: "25-34", ... }
// Special email method (saves to profile)
fox.inputs.setEmail('[email protected]');
const email = fox.inputs.getEmail();
// Listen to input changes
fox.inputs.subscribe((name, value) => {
console.log(`Input ${name} changed to:`, value);
});
// Listen to all input changes (same as subscribe)
fox.inputs.subscribeAll((name, value) => {
// React to any input change
});
Email binding: setEmail()
is special - it saves the email to the user’s
profile, not just as a regular input. Use this for primary email collection.
Stripe Integration
For Stripe-specific operations:
// Set customer ID for payment processing
fox.stripe.setCustomerId('cus_ABC123xyz');
Custom Tracking
Send events to third-party analytics:
// Track custom event with payload
fox.trackCustom('PricingCalculated', {
base_price: 99,
discount: 20,
final_price: 79,
currency: 'USD'
});
Note: Custom tracking events are sent to third-party services only.
They don’t appear in FunnelFox Analytics dashboard.
Event Listeners
React to funnel events:
// Listen for when saved replies are restored
fox.onRestoreReplies(() => {
console.log('User data restored from previous session');
// Update UI based on restored data
});
Working with the Raw HTML Element
Adding Custom Code
- Drag a Raw HTML element onto your screen
- Add your HTML and JavaScript:
<!-- Custom HTML -->
<div id="custom-calculator">
<h3>Your Result</h3>
<p id="result-text">Calculating...</p>
</div>
<script>
// Your JavaScript code
const weight = fox.inputs.get('weight')?.value;
const height = fox.inputs.get('height')?.value;
const bmi = weight / ((height/100) * (height/100));
document.getElementById('result-text').textContent =
`Your BMI is ${bmi.toFixed(1)}`;
fox.inputs.set('bmi', bmi.toFixed(1));
</script>
Execution Timing
- Code runs immediately when the screen loads
- No need for
DOMContentLoaded
or similar wrappers
- Fox API is available immediately
- Code runs every time user navigates to the screen
Accessing Other Elements
You can interact with other elements on the same screen:
<script>
// Hide/show elements based on logic
if (fox.inputs.get('age') < 18) {
document.getElementById('adult-content').style.display = 'none';
}
// Modify button behavior
document.getElementById('special-button')
.children[0]
.addEventListener('click', () => {
// Custom logic before navigation
if (validateForm()) {
fox.navigation.goNext();
}
});
</script>
Store Complex Data
<script>
// Create structured data from multiple inputs
const profile = {
demographics: {
age: fox.inputs.get('age'),
location: fox.inputs.get('location'),
income: fox.inputs.get('income')
},
preferences: {
plan: fox.inputs.get('selected-plan'),
addons: fox.inputs.get('selected-addons'),
billing: fox.inputs.get('billing-cycle')
},
score: calculateScore()
};
// Store as JSON string
fox.inputs.set('user-profile', JSON.stringify(profile));
</script>
Limitations & Warnings
Here be dragons: Custom code can break your funnel if not properly tested.
Always test thoroughly in preview mode before publishing.
Things to Know
- No sandboxing: Your code has full access to the page
- No error boundaries: JavaScript errors can break the funnel
- Performance impact: Heavy scripts can slow down navigation
- Cross-screen state: Code doesn’t persist between screens
- Browser compatibility: You’re responsible for compatibility
Testing Your Code
Preview Mode
Use preview mode to test without affecting production:
<script>
if (fox.sandbox) {
console.log('Running in preview mode');
// Test-specific code
fox.inputs.set('test-mode', true);
} else {
console.log('Running in production');
// Production code
}
</script>
Next Steps