Overview
This tutorial shows you how to create a multi-step onboarding carousel using a single HTML In-App Message. Unlike traditional carousels that rely on swipe gestures, this approach uses button-driven navigation and keeps all steps within one message. What you’ll build:- A two-step onboarding flow with images, text, and buttons
- Button navigation (tap “Next” to advance, tap “Get Started” to dismiss)
- Progress indicator dots
- Smooth fade transitions between steps

- Guide users through a short onboarding or education flow (2-5 steps)
- Require users to explicitly tap a button to continue (no swipe gestures)
- Keep everything inside one HTML In-App Message for simplicity
- Automatically dismiss the message when the flow is complete
This guide uses an HTML In-App Message for full control. You can also build card-based onboarding flows with the drag-and-drop editor—those cards are swipable but offer less customization.
Prerequisites
Before you begin, make sure you have:- An active OneSignal app with In-App Messages enabled
- Permission to create or edit HTML In-App Messages
- Mobile SDK installed in your mobile app
- Basic understanding of HTML, CSS, and JavaScript
How the multi-step flow works
Before diving into the code, it’s important to understand the technical approach. This implementation uses one HTML In-App Message that switches between steps by showing and hiding content, not by loading multiple separate messages. The architecture relies on four core components:Card containers for each step
Each step is wrapped in a
<div> with the card class and a unique ID:- All cards exist in the DOM simultaneously
- Only one card is visible at a time (controlled by the
activeclass)
CSS visibility control
CSS handles the show/hide logic using opacity and pointer events:Why this matters:
opacity: 0hides the card visually but keeps it in the layoutpointer-events: noneprevents accidental clicks on hidden cardstransitioncreates smooth fade effects
JavaScript state management
The This function:
setActive(i) function controls which card is visible:- Removes
activefrom all cards - Adds
activeto the target card - Updates progress indicator dots
Key insight: This is a single-page application (SPA) pattern within an In-App Message. All content is loaded once, and JavaScript manages state changes without reloading.
Step 1: Create a new HTML In-App Message
- In the OneSignal dashboard, go to Messages → In-App Messages
- Click New In-App Message
- Select HTML as the message type
- Choose Full Screen or Large layout (recommended for onboarding to maximize visual impact)
- Continue to the HTML editor
The HTML editor preview may not fully reflect runtime behavior. Always test on a real device or test user to verify animations, button behavior, and the dismiss action.
Step 2: Add the HTML template
Replace the editor contents with the template below. This template includes:- Self-contained code: All HTML, CSS, and JavaScript in one file
- Button-driven navigation: No swipe gestures (more reliable across devices)
- Fade transitions: Smooth opacity changes between steps
- OneSignal SDK integration: Uses
OneSignalIamApi.close(e)to dismiss the message - Mobile-optimized: Responsive layout with viewport meta tag
View complete HTML template
View complete HTML template
Step 3: Customize your content
Safe to customize
You can modify these elements without breaking functionality: Content:- Headline text in
<h1>tags - Body copy in
<p>tags - Button labels (
Next,Get Started) - Image URLs in the
background-image: url('...')styles
- Colors: Change
.btnbackground, text color, or dot colors - Spacing: Adjust padding and margins
- Typography: Modify font-family, font-size, font-weight
- Border radius: Update
border-radiusvalues for buttons and images
Adding more steps
To add a third step, follow this pattern:- Add the HTML card:
- Add a progress dot:
- Update the
setActive()function:
- Update the previous step’s button ID:
Change
id="done"toid="next-1"on card 1’s button, then add a click listener:
- Add the dismiss button to the new last card (card-2):
Step 4: Test the In-App Message
Testing checklist
- Save the message in the OneSignal dashboard
- Configure delivery settings:
- Set trigger conditions (e.g., session start, specific page view)
- Choose your target audience or select a test user
- Send to a test device:
- Use Test Users to preview without affecting production users
- Install your app on a physical device (recommended over simulators for accurate behavior)
- Verify functionality:
- ✓ First card appears with correct content
- ✓ “Next” button advances to card 2
- ✓ Progress dots update correctly
- ✓ Fade transitions are smooth
- ✓ “Get Started” button dismisses the message
- ✓ Message doesn’t reappear immediately (check frequency capping settings)
Simulators/emulators may not accurately reflect real device behavior, especially for touch interactions and SDK integrations. Always test on physical devices before launching to production.
Troubleshooting common issues
| Issue | Likely cause | Solution |
|---|---|---|
| Message doesn’t appear | Trigger conditions not met | Check In-App Message Triggers and verify your test user meets the criteria |
| Buttons don’t work | JavaScript errors or ID mismatch | Check browser console for errors; verify button IDs match event listener IDs |
| Images don’t load | CORS issues or invalid URLs | Use HTTPS URLs; test image URLs in a browser first |
| Message appears but won’t dismiss | OneSignal SDK not loaded | Verify Mobile SDK setup is complete |
Next steps
Track user engagement:- Add click tracking using
data-onesignal-unique-labelattributes (already included in the template) to measure drop-off between steps - View click analytics in Messages → In-App Messages → [Your Message] → Analytics
- Tag users who complete onboarding (e.g.,
onboarding_completed: true) - Use tags to segment users and prevent re-showing the onboarding flow
- Add user data to personalize content in future messages
- Deep link users to a specific screen after dismissal
- Use Liquid syntax to personalize headlines with user names or attributes
- Implement A/B testing with different onboarding flows to optimize completion rates