diff --git a/resources/views/docs/mobile/4/plugins/feature-bundles.md b/resources/views/docs/mobile/4/plugins/feature-bundles.md
new file mode 100644
index 00000000..fc077a50
--- /dev/null
+++ b/resources/views/docs/mobile/4/plugins/feature-bundles.md
@@ -0,0 +1,197 @@
+---
+title: Feature Bundles
+order: 725
+---
+
+## The Problem
+
+A plugin that wraps a large SDK usually only needs a slice of it. The Firebase plugin can send push notifications,
+report crashes, attest app integrity, trace performance, and display in-app messages — but an app that just wants push
+should not link the crash reporter, the attestation provider, and the performance instrumentation into its binary.
+
+Declaring everything at the top level of `nativephp.json` means every app pays for every capability: bigger downloads,
+more SDK initialization at launch, more permissions to justify in review, and more surface to break a build.
+
+**Feature bundles** let a plugin declare optional pieces of itself behind an environment variable. What an app doesn't
+turn on is never compiled in.
+
+## Declaring a Feature
+
+Add a `features` map to `nativephp.json`. Each entry is a miniature manifest gated by one environment variable:
+
+```json
+{
+ "namespace": "Firebase",
+ "bridge_functions": [
+ {"name": "PushNotification.GetToken", "ios": "PushNotificationFunctions.GetToken"}
+ ],
+ "ios": {
+ "dependencies": {
+ "swift_packages": [
+ {
+ "url": "https://github.com/firebase/firebase-ios-sdk",
+ "version": "12.6.0",
+ "products": ["FirebaseCore", "FirebaseMessaging"]
+ }
+ ]
+ }
+ },
+
+ "features": {
+ "crashlytics": {
+ "env": "NATIVEPHP_FIREBASE_CRASHLYTICS",
+ "description": "Crash reports plus PHP exceptions as non-fatals",
+ "bridge_functions": [
+ {
+ "name": "Crashlytics.RecordException",
+ "ios": "CrashlyticsFunctions.RecordException",
+ "android": "com.nativephp.firebase.CrashlyticsFunctions.RecordException"
+ }
+ ],
+ "ios": {
+ "info_plist": {"FirebaseCrashlyticsCollectionEnabled": false},
+ "dependencies": {
+ "swift_packages": [
+ {
+ "url": "https://github.com/firebase/firebase-ios-sdk",
+ "products": ["FirebaseCrashlytics"]
+ }
+ ]
+ }
+ },
+ "android": {
+ "dependencies": {"implementation": ["com.google.firebase:firebase-crashlytics"]}
+ }
+ }
+ }
+}
+```
+
+An app enables it in `.env`:
+
+```dotenv
+NATIVEPHP_FIREBASE_CRASHLYTICS=true
+```
+
+Then rebuilds. `php artisan native:plugin:list` shows the bridge functions appear and disappear as you flip the flag.
+
+## What a Bundle May Declare
+
+| Key | Behavior when enabled |
+|---|---|
+| `env` | The variable that gates the bundle. Omit it and the bundle is always on — useful purely for grouping. |
+| `bridge_functions` | Appended to the plugin's own. |
+| `events` | Merged with the plugin's own (deduplicated). |
+| `ios` | Merged into the plugin's `ios` section. |
+| `android` | Merged into the plugin's `android` section. |
+| `description` | Documentation for humans; ignored by the compiler. |
+
+Platform sections merge by shape: **lists concatenate** (permissions, capabilities, Gradle coordinates), **maps merge
+key-wise** (`info_plist`, `entitlements`, `meta_data`), and `dependencies` recurses so a feature can add packages
+without restating the plugin's.
+
+Resolution happens when the manifest is parsed, before anything reads it — so the compilers, `native:plugin:list`, and
+every other consumer see one effective manifest and need no awareness of features at all.
+
+## Feature Source Files
+
+Native sources for a feature live **outside** `resources/ios` and `resources/android`:
+
+```
+resources/
+├── ios/ # always compiled
+│ └── PushNotificationFunctions.swift
+├── android/ # always compiled
+│ └── PushNotificationFunctions.kt
+└── features/
+ └── crashlytics/ # only when the gate is on
+ ├── ios/CrashlyticsFunctions.swift
+ └── android/CrashlyticsFunctions.kt
+```
+
+
+
+Override the location per feature with `source_dir` if you need a different layout:
+
+```json
+"crashlytics": {
+ "env": "NATIVEPHP_FIREBASE_CRASHLYTICS",
+ "ios": {"source_dir": "resources/crashlytics-swift"},
+ "android": {"source_dir": "resources/crashlytics-kotlin"}
+}
+```
+
+## Sharing an SDK Package Between Features
+
+Several features often come from one SDK. Declare the **same package url** in each bundle, listing only the products
+that feature needs:
+
+```json
+"ios": {
+ "dependencies": {
+ "swift_packages": [
+ {"url": "https://github.com/firebase/firebase-ios-sdk", "products": ["FirebasePerformance"]}
+ ]
+ }
+}
+```
+
+The iOS compiler resolves a repeated url to the package reference already in the Xcode project and attaches the extra
+products to the target, so there is no duplicate package entry — and no need to restate the version.
+
+## Truthiness
+
+The gate is read with Laravel's boolean rules, so `true`, `1`, `"true"`, and `on` enable a feature, while `false`,
+`0`, `""`, and an unset variable leave it off. An unset variable is the default — features are opt-in.
+
+## What a Disabled Feature Costs
+
+Nothing. When the gate is off:
+
+- its native sources are not copied into the project
+- its SDK products and dependencies are not linked
+- its bridge functions are not registered — so no generated registration code references classes that were never
+ compiled
+- its `info_plist`, `entitlements`, `meta_data`, and permissions entries never appear
+
+The PHP surface of a feature (a facade, a service class) can still ship unconditionally: bridge calls degrade to a
+no-op off-device, so `Crashlytics::log()` in an app that never enabled the feature returns `false` rather than
+erroring.
+
+
+
+## Documenting Features for Your Users
+
+An app developer cannot discover a feature by reading code. List each variable in your plugin's README with what it
+adds and what it costs:
+
+```markdown
+| Variable | Adds |
+|---|---|
+| `NATIVEPHP_FIREBASE_CRASHLYTICS` | Crash reporting, PHP exceptions as non-fatals |
+| `NATIVEPHP_FIREBASE_PERFORMANCE` | Custom traces and automatic HTTP/startup metrics |
+```
+
+## Official Plugins & Dev Kit
+
+Skip the configuration complexity — browse ready-made plugins or get the Dev Kit to build your own.
+[Visit the NativePHP Plugin Marketplace →](https://nativephp.com/plugins)
diff --git a/resources/views/docs/mobile/4/plugins/permissions-dependencies.md b/resources/views/docs/mobile/4/plugins/permissions-dependencies.md
index 30f0ccea..b9d3976f 100644
--- a/resources/views/docs/mobile/4/plugins/permissions-dependencies.md
+++ b/resources/views/docs/mobile/4/plugins/permissions-dependencies.md
@@ -252,6 +252,12 @@ Here's a complete manifest for an ML plugin that uses Mapbox maps:
}
```
+## Making Dependencies Optional
+
+Everything above is compiled into every app that installs your plugin. When a dependency only serves part of what your
+plugin does — a crash reporter, an analytics SDK, a maps renderer — declare it in a
+[feature bundle](/docs/mobile/4/plugins/feature-bundles) instead, so apps link it only when they opt in.
+
## Official Plugins & Dev Kit
Skip the configuration complexity — browse ready-made plugins or get the Dev Kit to build your own.