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:1
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)
2
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
3
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
4
Button event listeners
Buttons trigger navigation or dismissal:Important:
OneSignalIamApi.close(e) is the OneSignal SDK method that dismisses the In-App Message from within the HTML.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
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