NSDrawer, Child Windows, and Modern macOS Applications

NSDrawer
is undoubtedly one of the more creative and iconic use cases for so-called "child windows" on macOS. Despite that, drawers have been deprecated in macOS 10.13 and seemingly forgotten since then. You can hardly find any use of drawers in any macOS applications these days. Visiting the NSDrawer
page on the modern Developer Documentation website shows a big and scary deprecation warning:
Drawers are deprecated and should not be used in modern macOS apps.
Funny enough, the class is implicitly marked @MainActor
.
Now, I'm by no means advocating going against the modern HIG and encouraging widespread adoption of drawers in the year 2025, but I'm a firm believer that the history of our tools, frameworks, and software is well worth studying and drawing inspiration from.
What are Drawers?
NSDrawer
is conceptually an auxiliary window that is attached to a visible window and moves with it. Drawers can be resized but can never get larger than their parent. A window may have multiple drawers associated with it and open at the same time.
Drawers can be open, closed, toggled, or in the process of opening or closing. When closed, they’re invisible. Drawers open on a specific edge of their parent, or a preferred edge if not specified. By default, drawers have a delightful animation for opening and closing, in which they appear from underneath and collapse into their parent window.

You can still find examples of drawers in some older OS X applications. UI Browser is the first example that comes to mind. It uses a drawer as an inspector for viewing individual attributes of a selected Accessibility element.

To me, this use of drawers doesn't really feel out of place – even on macOS Sequoia. In a modern application, you would've probably used an inspector panel, which would imply a more compact amount of horizontal space, or a vertical Split View. The drawer here does feel somewhat unusual, simply because it's not something you see across the system anymore, but it's honestly quite welcome and makes me feel in a certain Mac nerd type of way when using UI Browser.
Drawers on macOS Sequoia
As a developer in the Golden Age of drawers, you would use an AppKit class called NSDrawer
to create, present, and manipulate drawers. The API surface was fairly straightforward and very similar to that of an NSWindow
, albeit simplified. Method names like open
, toggle
, and close
are self explanatory and could get you started quickly.
If you are skeptical that NSDrawer
is indeed simply a window under the hood, you may convince yourself by using any tool available to you to inspect the application's window list. You will find that there is a one-to-one mapping between the conceptual representation of the NSDrawer
and its window, which has the NSDrawerWindow
type. The latter class is, of course, a framework implementation detail.

@interface NSDrawerWindow : NSWindow {
NSDrawer * _drawer;
NSWindow * _drawerParentWindow;
}
In the best traditions of AppKit's bincompat, the entirety of the API surface still works. You can still use drawers and compile code that uses them from all those years ago. Now, why wouldn't I want to play with them?
To have some fun with drawers, I went digging in the depths of the best resource known to an old school Apple developer, the Documentation Archive. I found a sample project aptly named DrawerMadness, originally published in May 2009 (more than 15 years ago!) and last updated in June 2012. Excited for some drawer madness, I proceeded to download it, open it in my Xcode 16.2 on macOS 15.2 and press Command-R. It built and ran on the first try, reminding me of one of the many reasons I love Objective-C.
Remember NSDrawer
s? This is them now. Feel old yet?
The sample app demonstrates the ability to open, close, and toggle multiple drawers along different edges of the window. Dragging the window while drawers are visible clearly still utilizes server-side dragging, and leads to a smooth dragging experience. Like any child windows, the drawers become part of the parent window's movement group. The window's behavior doesn't regress in any way due to the use of drawers.
Child Windows
There are, of course, many reasons why one should avoid using deprecated APIs in production code, but I believe drawers can serve as inspiration for more creative uses of the movement group mechanics of NSWindow
s. After all, you would be able to replicate the majority of the characteristic behaviors of NSDrawerWindow
at home by using the following methods.
First, you'd set up a drawer as a borderless auxiliary NSWindow
, then call addChildWindow
with the parent window as the receiver to present the drawer.
parentWindow.addChildWindow(drawerWindow, ordered: .below)
Now, you've gotten the behavior of the window moving with the parent window whenever it is dragged or moved programmatically without doing any additional work. What's even better is that you didn't have to opt out of server-side dragging to accomplish that. Your child window will also seamlessly participate in system behaviors like Window Tiling (try Globe-Control-Right Arrow).
We can even implement a simple tear-off behavior by recognizing a drag originating in the drawer, transforming its styleMask
and collectionBehavior
to be a more standard window, and call removeChildWindow
to detach the drawer from the movement group. Of course, once you're done, don't forget to order the window out and make sure it's properly deallocated. It's that simple.
We could further experiment with implementing fun animations for our custom drawers, allowing them to slide from underneath the parent window and perhaps even pop on the tear-off gesture. Perhaps that could be a fun exploration for another post?