Finder shows every Mac application as one icon with one size, but a .app is not a single file — it is a folder, styled by macOS to look and behave like one. Right-click any app and choose Show Package Contents and the illusion drops away: you are looking at a directory tree with an executable, resource files, bundled frameworks, and often a code signature, each contributing its own share to the number Finder reports. Understanding that structure explains why two apps that look similarly complex can differ in size by a factor of ten, why an update can suddenly double an app's footprint, and why deleting an app from /Applications does not always reclaim everything it used. This article walks through what is actually inside a bundle and what makes it grow.

A .app is a package, not a file

Technically, a .app is what macOS calls a package: a directory that Finder is told, via its extension and internal metadata, to present as a single double-clickable item rather than a browsable folder. Underneath, it is an ordinary directory on APFS or HFS+, and every rule that applies to folders applies to it — du -sh on an .app path will walk it recursively and sum every file inside, exactly as it would for any other directory. This is why an app's reported size in Finder's Get Info and its size from du should agree closely: both are summing the same underlying files.

What's typically inside

The standard layout, defined by Apple's bundle conventions, puts almost everything under Contents:

  • Contents/MacOS — the actual executable binary or binaries. For a single-window utility this might be a few megabytes; for a large application it can be far more, especially once you account for multiple architectures (below).
  • Contents/Resources — images, icons, .nib or .storyboard compiled interface files, localisation bundles (one subfolder per language), sounds, and other static assets the app loads at runtime.
  • Contents/Frameworks — any dynamic libraries or embedded frameworks the app ships with rather than relying on the system to provide. Apps built with cross-platform toolkits, or that bundle their own copy of a runtime, often carry the bulk of their size here.
  • Contents/PlugIns — extensions, app extensions, or plug-in bundles the main app loads conditionally. A share extension, a Spotlight importer, or a Quick Look preview generator typically lives here as its own nested bundle.
  • Contents/Info.plist — a small property list describing the app's identity, version, and capabilities. Negligible in size but essential to how the OS treats the bundle.

Large applications frequently carry multiple embedded frameworks, each one effectively a smaller bundle nested inside the outer one, and each contributing its own executable, resources, and sometimes its own code signature.

The code signature's own footprint

Every distributed Mac app is expected to carry a code signature, checked with tools like codesign. The signature is not free: it consists of cryptographic hashes computed over the executable's contents, stored either embedded in the binary or as separate signature data within the bundle, plus (for apps distributed outside the Mac App Store) an embedded provisioning or entitlement description. For a small utility this overhead is trivial — tens of kilobytes at most. For an app with many binaries, frameworks, and plug-ins, each one individually signed, the cumulative signature data across the bundle can add up to a measurable fraction of total size, particularly because every nested framework and plug-in typically carries its own signature rather than sharing one.

Universal binaries roughly double the executable

The biggest single factor separating a bloated-feeling app from a lean one is usually the executable itself, and specifically whether it is a universal binary. A universal binary contains complete machine code for more than one CPU architecture in a single file — commonly Apple Silicon (arm64) and Intel (x86_64) — glued together with the lipo format. macOS picks the slice it needs at launch time and ignores the other, but both slices are physically present in the file on disk.

Because each architecture's code is largely independent machine code for the same logic, a universal binary is roughly twice the size of a single-architecture build of the same app. You can inspect which architectures a binary actually contains — and confirm whether an app is truly universal or has quietly dropped support for one platform — with lipo -info against the executable inside Contents/MacOS. Some developers ship separate downloads (an Apple Silicon build and an Intel build) specifically to avoid this doubling; others ship one universal binary for simplicity, accepting the larger download and installed size as the cost of supporting both platforms from a single artifact.

Resources and frameworks usually dwarf the executable anyway

For most modern apps, the executable itself — even doubled by universal binary packaging — is not actually where the bulk of the size lives. Bundled frameworks, embedded language runtimes, high-resolution image assets shipped for every screen density, and localisation resources for dozens of languages typically account for more total bytes than the compiled code. This is why removing unused localisations or compressing image assets can meaningfully shrink a bundle, while stripping debug symbols from the executable alone often barely moves the needle for a resource-heavy app.

Deleting an app does not always reclaim everything

Because a .app bundle is self-contained, dragging it to the Trash and emptying the Trash reliably reclaims every byte inside Contents. What it does not reclaim is anything the app wrote outside its bundle while running: caches in ~/Library/Caches, preference files, and application support data in ~/Library/Application Support all persist after the app itself is gone, because macOS has no built-in mechanism that ties an app's bundle deletion to cleanup of its scattered library data.

Conclusion

An .app's size on disk is the sum of a small number of well-defined pieces — executable, resources, frameworks, plug-ins, and signature data — and understanding that breakdown turns "why is this app so big" from a mystery into an inspection you can actually do.

Seeing that breakdown clearly, rather than guessing at it, is exactly the kind of thing a proper storage map is for. VolumeLens treats an .app bundle as the folder it really is, so you can open one up and see which part of it — a bloated framework, years of accumulated localisations, or the app's own separate cache folder elsewhere on disk — is actually responsible for its footprint.