Loading tutorials…
Loading tutorials…
In-app messaging closes the gap email can't reach — the user who is actively using your product right now. Banners, modals, slideouts, and inline messages reach users at the exact moment of intent.
Who this is forSaaS teams with a working Customer.io email/workflow setup who want to add in-app messaging. Especially valuable for products with daily active usage — onboarding nudges, feature announcements, upsell prompts, and retention messages live and breathe inside the product itself.
What you'll need
Step 1
Web: add the Customer.io JS snippet + in-app SDK to your authenticated app. Mobile: integrate the Customer.io mobile SDK.
**Web setup**: Add the Customer.io JS snippet (from Settings → JavaScript Snippet) to your authenticated app shell — NOT public marketing pages. The snippet must run after the user is identified.
After loading the snippet, call `_cio.identify({ id: user.id, email: user.email, ... })` in your app's authenticated layout. This is what tells Customer.io who the current user is.
Add the in-app messages SDK: `<script>` tag with the in-app SDK URL. Customer.io provides this in Settings → In-App Messages → JavaScript snippet.
**Mobile setup**: Already integrated Customer.io mobile SDK for push? In-app uses the same SDK. iOS: `CustomerIO.shared.identify(...)` triggers in-app fetching. Android: `CustomerIO.instance().identify(...)`.
Verify: log into your app as a test user → trigger an in-app from Customer.io (Send Test) → confirm the message renders in the app.
Step 2
Customer.io supports: Banner (top/bottom bar), Modal (centered overlay), Slideout (corner card), Inline (embedded in page content). Each has best-use cases.
**Banner** (top or bottom bar): Use for persistent low-priority info — trial countdown, system status, gentle nudge to complete profile. Stays until dismissed.
**Modal** (centered overlay): Use for high-priority moments that demand action — major feature announcement, plan-must-upgrade, account warning. Use sparingly; modals interrupt.
**Slideout** (corner card, usually bottom-right): Use for contextual nudges — "Try the new AI feature," "Invite a teammate." Less intrusive than modal, more visible than banner.
**Inline** (embedded in page content via a placement ID): Use for in-context content — empty-state CTAs, in-flow help, integrated onboarding hints. Most "product-feels-native" option.
A healthy SaaS in-app program uses 60-70% inline + slideout, 20-30% banner, <10% modal. Modal overuse trains users to reflexively dismiss.
Step 3
Match the product UI. Use your brand colors, typography, and component patterns. Don't look like a marketing popup.
Customer.io → In-App Messages → + Create. Pick template type (Banner/Modal/Slideout/Inline).
Edit content: title, body, CTA button text, CTA URL. Customer.io provides default templates — replace with your brand styling via CSS.
Add a custom CSS block: match your app's font family, button styles, border-radius, color palette. The default Customer.io styling looks generic — investing 30 min in custom CSS dramatically improves engagement.
For the modal: include a clear dismiss (X) button. Forced-action modals (no dismiss) generate complaints.
For inline messages: define placement IDs in your code where messages can appear. E.g., `cio.inline.placement('dashboard_empty_state')` in your React component renders any active inline message targeted to that placement.
Preview on real devices: mobile screens have different constraints than desktop. Modals on mobile should NOT use desktop dimensions — switch to bottom sheet or full-screen on small viewports.
Step 4
Journeys → add In-App block alongside email/SMS/push. In-app messages are delivered when the user is next active in the app.
Open a Journey workflow. Drag an In-App block from the toolbox.
Configure: pick the in-app message you built. Set frequency cap (e.g., show once per user, ever).
Set the showing condition: "show on next session," "show within 7 days," etc. After the window, message expires unshown.
For inline messages: target a specific placement ID. The message only renders when the user visits a page with that placement.
Test: enter the workflow as a test user → open the app → verify the in-app appears at the right moment.
Step 5
Activation nudge, feature adoption, upgrade prompt, and feedback request — these four cover 80% of SaaS in-app value.
**Activation Nudge**: Trigger = user signed up 24h ago but hasn't hit activation event. In-App = inline placement on dashboard, "Here's how to get started." Closes the email-to-activation gap.
**Feature Adoption**: Trigger = user is on Pro plan but hasn't used Feature X in 30 days. In-App = slideout when they next visit Feature X area: "Try the new {{ feature_name }} workflow." Lifts feature adoption 10-20%.
**Upgrade Prompt**: Trigger = user is on Free plan AND hit Free limits AND has used product 7+ days. In-App = modal explaining what unlocking Pro gets them. Conversion rate: 3-8% of triggered users.
**Feedback Request**: Trigger = user has been active 30 days, hasn't given NPS feedback. In-App = slideout NPS widget. Response rate: 20-35% vs 2-5% for email NPS.
Each workflow: clear trigger, clear goal, frequency cap (don't re-prompt for at least 30 days).
Step 6
Even one bad in-app experience ruins the channel. Cap show frequency aggressively. Honor dismissals.
Workspace-wide cap: max 1 in-app message per user per app session. No exceptions.
Per-message cap: most messages should show once per user, ever. Some can show weekly (e.g., trial countdown).
When user dismisses a message, don't re-show for 30+ days. Customer.io tracks dismissals automatically — use this in workflow filters.
Track in-app metrics: shown, clicked, dismissed. Dismissal rate above 70% means the message is wrong audience/wrong moment — pause and rebuild.
Quarterly audit: in-app messages older than 90 days that aren't critical should be archived. Keeps the workspace clean.
Step 7
In-app analytics differ from email. Track impression rate, click-through, dismiss rate, goal completion.
Customer.io → Analytics → In-App Messages → per-message stats.
Healthy in-app benchmarks: Impression rate (users who saw it / users who triggered it) 60-90% — most miss this because they didn't visit the app in the window.
Click rate: 8-25% for slideouts, 15-40% for inline. Modal click rate is misleadingly high (users click to dismiss) — track meaningful action separately.
Compare to email: in-app typically outperforms email for activation campaigns by 2-3x in conversion rate (because user is already in product).
In your data warehouse / product analytics, tag users who converted via in-app vs email separately so attribution is clean.
Common mistakes
In-app modals that block the entire UI without a dismiss option
What goes wrong: Users rage-tap to dismiss, can't, force-quit the app. Support tickets spike 300% for 'why is this thing blocking my work.' App Store reviews mention 'pushy marketing.' Long-term NPS damage that takes months to recover.
How to avoid: Every modal has a visible X dismiss button. ESC key dismisses on web. Clicking outside the modal dismisses. Always.
Generic Customer.io styling that screams "marketing tool"
What goes wrong: Users recognize the generic look and reflexively dismiss without reading. Engagement drops 50-70% vs natively-styled messages. The channel value evaporates.
How to avoid: Invest 1-2 hours in custom CSS matching your app's design system. Use your fonts, colors, button styles, border-radius. The first 3 messages set the precedent — get them right.
No frequency caps, users see 4 in-app messages in one session
What goes wrong: Users dismiss everything reflexively after the first 2. Future in-app messages — even important ones (security alerts, system status) — get dismissed unread. Channel becomes useless within 30 days.
How to avoid: Workspace cap: max 1 in-app per session. Per-message cap: once per user ever (or rarely, weekly). Audit overlapping workflows for in-app frequency conflicts.
Triggering in-app messages without identify firing first
What goes wrong: Anonymous users with no identification get the in-app, but the message references {{ first_name }} or {{ plan_name }} — renders broken. Or worse: the message targets paid users but shows to free trial users because Customer.io doesn't know yet.
How to avoid: Ensure identify call fires on app load (after auth check). Only load the in-app SDK after identify completes. Test specifically with brand-new sessions to verify the identify-before-in-app sequence.
Modal on mobile sized for desktop
What goes wrong: Modal renders 600px wide on a 375px screen. Buttons get cut off. Users tap dismiss because they can't reach the CTA. Mobile users — often 50%+ of B2C SaaS traffic — get zero value.
How to avoid: Design mobile-first: modals at 90% viewport width on mobile, max 480px on desktop. Test on real devices. Customer.io's preview pane is approximate — actual devices catch issues simulators miss.
No analytics on dismissals, ignoring user feedback
What goes wrong: A message gets dismissed by 90% of triggered users — clear signal it's wrong audience or wrong moment. But nobody checks the metric. Message keeps running for 6 months, training thousands of users to dismiss your in-app.
How to avoid: Weekly review: in-app messages with dismiss rate >70% → pause, redesign, or re-target. Don't let bad messages run.
Recap
Done — what's next
How to build Customer.io segments and Journeys workflows that actually fire
Read the next tutorial
Hand it off
In-app messaging design is one of the highest-leverage areas for a specialist. A specialist who's launched 15+ in-app programs designs the CSS, builds the 4 core messages, integrates with workflows in 1 week — typically $1,000-2,500 at $14-16/hr. Engagement lift from properly-designed in-app is usually 3-5x DIY work.
See specialist rates
Premium and above. In-app messaging is not included in Essentials. If you're on Essentials and need in-app, the upgrade to Premium ($300/mo+ depending on profile count) is the cleanest path. Alternative tools at the Essentials tier: Userpilot, Pendo, or Appcues — separate platforms not integrated with Customer.io workflows.
For pure messaging use cases — yes, mostly. Customer.io covers banner/modal/slideout/inline. The gap is interactive product tours (multi-step walkthroughs with highlighted UI elements) — Userpilot/Pendo do this better. For most SaaS lifecycle teams, Customer.io in-app + a lightweight tour tool covers the use cases.
Customer.io mobile SDK fetches in-app messages when the user authenticates. Messages cache locally. When the app meets the trigger condition (next session, specific screen, etc.), the message renders. Works offline once cached. Native iOS/Android, React Native, Flutter all supported.
Marginally. The in-app SDK adds 20-40KB gzipped to your bundle. Render-blocking is minimal (script loads async). If you're shipping in-app from Customer.io alongside heavy analytics SDKs (Mixpanel, Amplitude, etc.), watch total third-party JS weight — aim for total third-party under 200KB.
Yes. Inside a Journey with an In-App block, drop a Random Branch upstream and use different in-app message variants per branch. Performance tab shows engagement per variant. Same A/B test pattern as email but with in-app delivery.
Tricky. If your product has multi-workspace users (one user, multiple workspaces), pass the active workspace as an event property on identify or via custom attribute. Target in-app messages with workspace filters. Otherwise messages fire on workspace-A context but show in workspace-B, looking broken.
Customer.io
Segments are how Customer.io decides who. Workflows are how it decides when and what. Most SaaS teams get one or the other right but rarely both — and the gap shows up as activation campaigns missing 40% of eligible users.
Customer.io
Customer.io's email side has three distinct surfaces — Broadcasts, Newsletters, and Transactional — and each has its own rules. Mix them up and you'll send marketing content from your transactional IP (bad) or transactional from your marketing IP (worse).
Customer.io
Email + SMS + Push is the canonical SaaS lifecycle trifecta. Adding SMS lifts onboarding completion 8-15%. Adding push lifts re-engagement on mobile apps 20-35%. The setup is one weekend of work, ongoing.
Customer.io
Liquid is what turns Customer.io from 'mass blast tool' into 'one-to-one personalization at scale.' Knowing the right 20% of Liquid syntax — variables, defaults, conditionals, filters — covers 95% of SaaS personalization use cases.
Customer.io
Customer.io is only as smart as the events you send it. Sloppy instrumentation — events fired client-side, inconsistent naming, missing properties — silently sabotages every workflow you build on top. This is the schema that scales.
Customer.io
DIY Customer.io is the right call — until it isn't. In healthy SaaS, lifecycle email + in-app should drive 20-35% of activation and 10-20% of retention. If yours is at 5-10%, the gap is the program isn't being worked. Here's the honest framework for when to hire.