# In-app Announcements: Feature News Banners for SaaS Apps

> In-app announcements are dismissible banner strips inside your product — across the top or bottom of the page, full-width or floating — carrying a short message, an icon or emoji, and one optional button. In StepsKit they are written in a dashboard, audience-targeted, scheduled, frequency-capped, themed, and published per domain without a deploy.

*Source: https://stepskit.com/features/in-app-announcements*

## Definition

An in-app announcement is a page-level broadcast: one banner for news that isn't tied to a specific UI element — a launch, a price change, a maintenance window, an offer. It sits between StepsKit's other surfaces: a [product tour](/features/product-tours.md) walks a user through a workflow step by step, and an [in-app tooltip](/features/tooltips.md) waits on a specific element until someone asks.

The channel is the point. A launch email competes with every other email; a changelog waits for a visit that rarely comes. A banner reaches users inside the product, where acting on the news is one click away — and because it's targeted and frequency-capped, it reaches the right users once, not all users forever.

## Positions

Four positions, all taking the same content, theme, and targeting:

| Position          | What it renders                                                                       |
| ----------------- | ------------------------------------------------------------------------------------- |
| `top`             | Full-width strip across the top of the page — the classic launch banner (the default) |
| `bottom`          | Full-width strip along the bottom edge                                                |
| `top-floating`    | A centered card below the top edge, max 720px wide, with a drop shadow                |
| `bottom-floating` | The same card at the bottom of the viewport                                           |

Full-width strips push the page down so nothing gets covered; floating cards hover over it with a shadow.

## Capabilities

| Capability        | Detail                                                                                                                                                                                                                             |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Content           | Optional title (max 200 chars) and a body (max 5000 chars), plain text with emoji. Rendered as text, never markup — pasted HTML shows up as characters, so a banner can never inject anything into your app.                       |
| Icon              | One of 24 curated icons (megaphone, gift, rocket, alert, bell, …) or any emoji, shown in a small tinted disc.                                                                                                                       |
| Button            | One optional CTA: `link` opens a URL in a new tab (validated against an https/http/mailto/tel allow-list, opened with `noopener`), or `custom` fires an `announcement_clicked` event your own JavaScript handles via `window.stepskit.on()`. |
| Dismissible       | On by default. Turned off, no close control renders at all — for maintenance or compliance notices that shouldn't be waved away.                                                                                                     |
| Corner radius     | `square`, `rounded`, or `pill` — set independently for the banner and its button. A `pill` banner applies to floating positions; full-width strips clamp it to `rounded`.                                                            |
| Theming           | Four colors — banner background, text, button background, button text. Project-wide default, overridable per banner.                                                                                                                |
| Schedule          | Optional `start_at` / `end_at` window, enforced server-side — outside it the announcement isn't even sent to the page. Either edge can be open.                                                                                     |
| Targeting         | URL-pattern globs (`/reports/*`), audience rules on any attribute passed via the embed (twelve operators, AND/OR), and a minimum screen width.                                                                                       |
| Frequency capping | `show_once` per visitor. Identified visitors are capped across sessions server-side; anonymous visitors fall back to session storage.                                                                                               |
| Publishing        | Per hostname, not a global switch — push to staging, then production, and retract either independently. One announcement shows at a time; the earliest-created match wins.                                                           |
| Install           | Rides the same embed and payload as tours, tooltips, and surveys. No separate install, no extra package, no extra request.                                                                                                           |

## How it works

1. **Write the banner.** Pick a position, type the message (emoji autocomplete included), choose an icon. A live preview renders the real banner beside the form as you type.
2. **Add the button.** A label plus a link to your changelog or docs, or a custom event key your own code listens for — or skip the button entirely.
3. **Target and schedule.** URL patterns, audience rules, minimum screen width, show-once capping, and the start/end window so it takes itself down.
4. **Publish per domain.** Staging first if you like, then production. Live on the next page load — and editable or retractable just as fast.

## Built for real apps

- **Pushes your layout, doesn't cover it.** The banner measures its own rendered height and pads the page by exactly that much — then finds fixed and sticky elements (navs, sidebars) and offsets them too. Dismissal restores every value it touched; a `ResizeObserver` re-applies the offsets when fonts load or the viewport changes.
- **Your CSS can't touch it.** The banner renders in a closed shadow root with all styles reset — your stylesheets can't restyle it and its styles can't leak out.
- **Plain text, on purpose.** Title and body render via `textContent`, never `innerHTML`. Button URLs are re-validated at click time and always open in a new tab with `noopener`.
- **Survives your SPA.** On a client-side route change the embed re-evaluates URL rules against the payload it already has — no refetch, and a still-matching banner stays put instead of re-rendering. No flicker, no re-initialization.

## AI agents

StepsKit ships an MCP server. An agent such as Claude Code or Cursor can call `create_announcement`, `list_announcements`, `get_announcement`, `update_announcement`, and `delete_announcement` — content, position, icon, button, schedule, targeting, and theme included — so the agent that shipped the feature can draft its launch banner in the same session. Agents create announcements **unpublished**; publishing stays a deliberate step in the dashboard. See [AI agents](/features/ai-agents.md).

## Choosing between surfaces

| Surface                             | Sequential? | Anchored?         | Use when                                           |
| ----------------------------------- | ----------- | ----------------- | -------------------------------------------------- |
| Announcements                       | No          | Page-level banner | Broadcasting news that isn't tied to one element   |
| [Tooltips](/features/tooltips.md)      | No          | Per-element       | Persistent contextual help that waits for the user |
| [Tours](/features/product-tours.md)    | Yes         | Per-step          | Walking a user through a workflow in order         |

Pick **announcements** when the news matters to a whole audience and isn't attached to one button. Pick **tooltips** when the help belongs next to the thing itself. Pick **tours** when there's a workflow to learn in order. They compose: the banner announces the launch, and its button can hand off to whatever shows it.

## Use cases

- **Launch day.** A NEW banner across the top of the app linking to the changelog entry, capped to once per visitor.
- **A maintenance notice that takes itself down.** A dark, non-dismissible strip scheduled to appear three days before the window and disappear when it closes.
- **An upgrade nudge only free users see.** A floating banner targeted to `plan = free`, whose button fires a custom event that opens your own upgrade dialog.

## FAQ

**What is an in-app announcement?**
A banner strip inside your product — top or bottom, full-width or floating — carrying a short message, an optional icon, and one optional button. Written in a dashboard, targeted, scheduled, and published without a deploy.

**How is an announcement different from a tooltip or a product tour?**
An announcement is a broadcast: one page-level banner for news not tied to a specific element. A tooltip is anchored to one element and waits for the user. A tour is a step-by-step sequence. All three share the same embed, targeting rules, and theming.

**Do I need a deploy to publish or change a banner?**
No. Changes reach visitors on their next page load. Publishing is per domain, so you can check the banner on staging before production.

**Can I schedule an announcement in advance?**
Yes — a start time, an end time, or both, enforced server-side. Outside the window the announcement isn't included in the payload at all.

**What can the button do?**
A `link` button opens a URL in a new tab (https/http/mailto/tel only, with `noopener`). A `custom` button fires an `announcement_clicked` event your own JavaScript handles via `window.stepskit.on()`.

**Can I put images or HTML in the banner?**
No — by design. Icon (or emoji), title, body, button, rendered as plain text so nothing can inject markup into your app. Emoji work anywhere in the text. If a launch needs more than a headline and a link, the banner's job is to carry the click to the page that has the room.

**How does show-once work for anonymous visitors?**
Identified visitors who dismiss a capped banner won't see it again — recorded server-side, across sessions and devices. Anonymous visitors fall back to session storage: dismissed for the rest of the tab session. Identification is one `setUserAttributes` call or a `data-user-id` attribute on the embed script.

**What if several announcements match the same page?**
One banner shows at a time, by design — the earliest-created announcement that matches the page, audience, and schedule wins. StepsKit doesn't stack banners.

**Can an AI agent create announcements for me?**
Yes, via the StepsKit MCP server. Agents create announcements unpublished; publishing stays in the dashboard.

**What does it cost?**
The free plan includes one announcement (plus one tour and one tooltip group). Paid is $19/month flat with unlimited announcements, tours, tooltips, and surveys — no per-user pricing.

## Related

- [All features](/features.md)
- [In-app tooltips](/features/tooltips.md) · [Product tours](/features/product-tours.md) · [AI agents](/features/ai-agents.md)
- [Feature adoption](/solutions/feature-adoption.md) · [User onboarding](/solutions/user-onboarding.md)
