Library / SDK
exyte/PopupView avatar
exyte/PopupView

exyte/PopupView: SwiftUI Toasts and Popups Without the Boilerplate

Toasts and popups library written with SwiftUI

4,061 stars315 forksSwiftMIT

At a glance

What is it?
PopupView is an MIT-licensed SwiftUI library for floaters, toasts, popups and sheets. It offers three display backends, and the choice between them decides what your popup can appear on top of.
Who is it for?
Adopt exyte/PopupView if you are building a SwiftUI app that needs toasts, floaters or modal popups and you want a single .popup(isPresented:) modifier instead of hand-rolled ZStack overlays. Skip it if your popups must live inside a single view hierarchy with plain @State driving their content, because the window display mode documented as best for most situations does not propagate @State updates; use the overlay mode there, or keep your own presentation code.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem PopupView solves for SwiftUI developers

SwiftUI ships with .sheet, .alert and .fullScreenCover, and none of them is a toast. A transient banner at the top of the screen, a floating card that stays put while the user keeps tapping the view underneath, or a stack of two popups layered over a navigation bar all require you to build presentation yourself: a ZStack, a transition, an animation, a hit-testing rule, and a way to dismiss on tap or drag. PopupView packages that work behind one view modifier, .popup(isPresented:), with a customize closure that sets the popup type, position, display mode and dismissal behaviour. The README frames the library as covering four visual families: floaters, toasts, popups and sheets. It is aimed at SwiftUI app developers who want the presentation layer handled and the styling left to them, since the popup body is any SwiftUI view you supply.

Three display backends and what each one can sit on top of

The mechanism that matters here is not the animation, it is where the popup is attached. Version 4 introduced three display modes, and the README gives a comparison table for them. Overlay cannot show on top of a navigation bar or on top of a sheet, but it can show multiple popups, lets taps pass through the transparent background, and keeps SwiftUI @State updates working. Sheet mode can show on top of a navbar, cannot stack multiple popups, cannot pass taps through, and keeps @State working. Window mode, which wraps UIKit's UIWindow, can do everything in the table except one thing: the README marks SwiftUI's @State update mechanism as not working as expected. The README states that the UIWindow-based popup is the best option for most situations, with the caveat that you should use ObservableObjects or @Bindings instead of @State for the popup's own content. That is a real architectural constraint rather than a footnote. A popup rendered in a separate UIWindow is outside the view tree that owns your @State, so a toggle inside the popup body will not re-render the way it would in an overlay. The README shows both the broken and the working version of the same counter-style example, and the fix is to move the popup body into its own view that takes a @Binding.

Installing PopupView with Swift Package Manager

The repository is a Swift package: Package.swift sits at the top level next to Sources/ and PopupExample/. The README carries an SPM compatibility badge and a CocoaPods badge marked deprecated after 4.0.0, so Swift Package Manager is the path the project endorses going forward. In Xcode, add the repository URL as a package dependency through File, Add Package Dependencies:

bash
https://github.com/exyte/PopupView

Alternatively declare it in your own Package.swift as a package dependency on the exyte/PopupView repository, then add PopupView to your target's dependencies. The README does not spell out a minimum deployment target in its text; the Swift Package Index platform badge linked from the README is where that information lives. Once the package resolves, import it in the file where you attach the modifier.

A first popup: the version 5 modifier in practice

The version 5 release notes describe the API as it stands now. Popup types moved out of the generic main class, so you write Popup.DisplayMode rather than Popup<V>.DisplayMode, which makes the types storable in variables. DismissSource became Popup.DismissSource. The scroll popup left the PopupType enum and became its own modifier. A minimal floater follows the structure the README uses in its examples: a @State boolean drives isPresented, the popup body is a separate view, and customize sets the type and position.

swift
struct ContentView: View {
    @State var showPopup = false

    var body: some View {
        Button("Button") {
            showPopup.toggle()
        }
        .popup(isPresented: $showPopup) {
            Text("Hello")
        } customize: {
            $0
                .type(.floater())
                .position(.top)
        }
    }
}

When the button is tapped, the boolean flips and the floater animates in at the top of the screen. Tapping outside dismisses it, because closeOnTap is left at its default. If you want the popup to stay until the user acts on it, chain .closeOnTap(false) as the README does in its window-mode example. The popup body here is a plain Text, which is fine because nothing inside it needs to update independently. The moment the body holds its own state, the window-mode caveat applies.

The @State failure mode in window mode

This is the sharpest edge in the library, and the README documents it directly rather than leaving it to be discovered. In window mode, a popup body that reads @State declared in the parent view will not update. The README's first example declares both showPopup and a inside ContentView, renders the popup inline, and toggles a from a button inside the popup. It does not work. The second example moves the body into a PopupContent struct that takes @Binding var a: Bool, and that version does work. The practical consequence is that window mode, the mode the README recommends for most situations, imposes a structural rule on your code: popup content must be a separate view fed by bindings or observable objects. If your popup is a small inline closure that reads a handful of parent @State values, you either refactor it or you pick overlay mode and accept that it cannot draw over a navigation bar or over a sheet. There is no mode in the table that gives you both.

Alternatives and where PopupView sits against them

The related searches around this project surface several other SwiftUI presentation libraries, including MijickPopups and LnPopupUI, plus the broader SwiftUIKit. The difference that matters is the display-backend question. A library that presents popups purely as SwiftUI overlays inside your existing hierarchy keeps @State semantics intact and composes naturally with SwiftUI previews, but it inherits the same limits the README lists for overlay mode: it cannot appear above a navigation bar or above a sheet. PopupView's distinguishing move is offering the UIKit UIWindow backend as a first-class option, which is what lets a popup cover a navbar or a sheet and stack with other popups. That capability is bought with the @State restriction. If your app never needs a popup above a navbar or a sheet, a pure-overlay approach is simpler and you avoid the binding refactor entirely. If it does, you need something window-backed, and the README's table is the clearest statement of what you are trading away.

Maintenance, licensing and the cost of the 4.x to 5.x migration

The repository is not archived, and the last push was on 2026-09-23. There are no retrieved releases to point at, so the README's version 5 section is the best available description of the current API. The licence is MIT, which permits commercial and closed-source use with the licence and copyright notice retained; that is a statement about the licence text, not legal advice, and you should read LICENSE in the repository. The upgrade cost is real and worth budgeting. Version 4 replaced isOpaque with a DisplayMode enum and deprecated the old property, and version 5 moved popup types out of the generic class, renamed DismissSource to Popup.DismissSource, and split the scroll popup into its own modifier with a header and customize closure. Anyone on 3.x or 4.x is facing renames at call sites, not a drop-in bump. The README does not document a rollback path or a compatibility shim for the old names, so pinning a version in your package manifest is the only lever it gives you.

Editorial conclusion

Adopt exyte/PopupView if you are building a SwiftUI app that needs toasts, floaters or modal popups and you want a single .popup(isPresented:) modifier instead of hand-rolled ZStack overlays. Skip it if your popups must live inside a single view hierarchy with plain @State driving their content, because the window display mode documented as best for most situations does not propagate @State updates; use the overlay mode there, or keep your own presentation code. Before committing, verify the display mode you need against the README's comparison table, confirm your minimum iOS version against the Swift Package Index platform badge, and check that the version 5 API names (Popup.DisplayMode, Popup.DismissSource, the separate .scrollPopup modifier) match the major version you resolve to, since the 4.x to 5.x renames are breaking.

Frequently asked questions

How do I install exyte/PopupView in a SwiftUI project?

Add it as a Swift package. The repository ships a Package.swift at the top level and the README carries an SPM compatibility badge, while the CocoaPods badge is marked deprecated after 4.0.0. The README does not list a minimum deployment target in its text; the Swift Package Index platform badge linked from the README is where that information lives.

How do I use exyte/PopupView to show a popup?

Attach the .popup(isPresented:) modifier to a view, pass a binding that controls visibility, supply the popup body as a SwiftUI view, and use the customize closure to set the type, position and dismissal behaviour. The README's examples chain .type(.floater()), .position(.top) and .closeOnTap(false) inside that closure.

Why does my exyte/PopupView content not update its @State?

The README states that in the UIWindow-based display mode, SwiftUI's @State update mechanism does not work as expected. The fix it gives is to move the popup body into its own view that receives a @Binding or an ObservableObject instead of reading the parent's @State directly. Overlay and sheet display modes keep @State working.

Can exyte/PopupView show a popup on top of a navigation bar or a sheet?

Only in sheet or window display mode. The README's comparison table marks overlay mode as unable to show on top of a navbar or on top of a sheet, while window mode can do both. Window mode is also the only mode that can show multiple popups and pass taps through the transparent background while keeping @State broken.

Official sources

  1. exyte/PopupView on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/exyte-popupview.svg)](https://hysenlabs.com/projects/exyte-popupview)