Claude Code for Mobile and Desktop Apps: CLAUDE.md Rules
TL;DR
- Claude Code writes app code that builds, but by default it gets the boundaries wrong: secrets bundled into the client, native changes shipped as over-the-air updates, permissions used without usage descriptions, generated native projects edited by hand, and desktop renderers given far more power than they need.
- One CLAUDE.md fixes that for any framework. Put the shared rules (the native boundary, client-side secrets, permissions, release paths) in one skeleton, then add a short block for the framework you use.
- The seven frameworks split into three families: React-based mobile (Expo, React Native), other cross-platform shells (Flutter, Capacitor) and native or desktop (Swift, Electron, Tauri). The rules that matter most differ by family: update compatibility for Expo, platform channels for Flutter,
cap syncfor Capacitor, project files and concurrency for Swift, and IPC security for Electron and Tauri. - Every framework fact in this guide was checked against that framework's documentation or the npm registry on 21 October 2026.
Claude Code can build and maintain a cross-platform app with very little supervision, as long as your CLAUDE.md tells it where the native boundary is and what may never cross it. This guide gives you that file. It starts with the skeleton every app shell shares, compares Expo, React Native, Flutter, Capacitor, Swift, Electron and Tauri side by side, and then lists only the rules specific to each, so you copy one shared block plus one framework block.
If you are new to project configuration, our guide to CLAUDE.md explains how Claude Code loads the file at the start of every session. For the React side of a React Native or Electron app, Claude Code with React covers component and hook conventions.
Why Claude Code needs rules for app shells
Apps have something web projects mostly do not: two worlds that are built and shipped differently. JavaScript, Dart or web assets on one side; compiled native code, permissions and signing on the other. Claude Code sees one codebase and cannot tell from the types which change needs a store release, which value ends up readable inside the shipped binary, or which API call will crash because a permission string is missing.
Across all seven frameworks, the same six failure patterns show up when there is no CLAUDE.md:
- Secrets in the client. Claude puts an API secret in an environment variable that is compiled into the app. Anyone who downloads the app can read it.
- Native changes shipped as updates. Claude adds a native module and pushes the change through an over-the-air or web-asset update, and the installed binary crashes because the native code is not there.
- Missing permission declarations. Claude calls the camera or location API without the matching iOS usage description or Android manifest entry.
- Hand-edited generated projects. Claude edits files under
ios/orandroid/that a generator will overwrite on the next run, or patches an Xcode project file directly. - Too much power in the UI layer. In desktop apps, Claude exposes raw IPC or enables Node.js in the renderer because it is the shortest path to reading a file.
- Unpinned toolchains. Claude installs the latest version of a package rather than the version compatible with the framework release you are on.
Which app framework should you use with Claude Code?
All seven work well with Claude Code; the right one depends on your team's language and which platforms you ship to. The table compares what changes how you write and release the app. Versions are current releases as of 21 October 2026.
| Family | You write | Native access | Shipping changes | Choose it when | |
|---|---|---|---|---|---|
| Expo (SDK 58) | React mobile framework | TypeScript and React | Expo modules and config plugins; native projects generated by prebuild | EAS Update for compatible JS changes; a new build for native changes | You want React Native with managed builds, updates and the least native setup |
| React Native (0.88) | React mobile runtime | TypeScript and React, plus native code where needed | Native modules on the New Architecture (default since 0.76) | Store builds; updates through a framework such as Expo | You need full control of the native projects or are integrating into an existing native app |
| Flutter | Cross-platform UI toolkit | Dart | Platform channels, ideally generated with Pigeon | Store builds | You want one rendering engine and one language across mobile and desktop |
| Capacitor (8) | Native shell for web apps | Any web framework | @capacitor/* and community plugins |
Rebuild the web app, npx cap sync, release |
You have a web app and want it in the app stores with native APIs |
| Swift | Native Apple | Swift and SwiftUI | Direct platform APIs | Xcode builds | You ship only on Apple platforms and want native performance and APIs |
| Electron (45) | Desktop shell | JavaScript or TypeScript with Chromium and Node.js | Main process and preload scripts over IPC | Packaged installers; auto-update requires code signing | You need a desktop app with full Node.js and a consistent Chromium renderer |
| Tauri (2) | Desktop shell | Rust backend, web frontend | Rust commands and plugins, gated by capabilities | Bundled installers; the updater requires signed artifacts | You want a smaller desktop app with a Rust backend and an explicit permission model |
Sources: Expo changelog (SDK 58, 15 October 2026), React Native architecture, Flutter platform channels, Capacitor sync, Electron packaging, Tauri updater. Package versions for react-native, @capacitor/core, electron and @tauri-apps/api from the npm registry. All read on 21 October 2026.
A quick way to choose:
- Expo if you are a React team starting a new mobile app. React Native's own documentation recommends using a framework for new apps and names Expo (React Native environment setup).
- Flutter if you prefer Dart and want the same widgets everywhere.
- Capacitor if the web app already exists and the goal is store presence plus device APIs.
- Swift if the product is Apple-only.
- Electron if desktop users need deep Node.js integration and you accept a larger app.
- Tauri if you want a lighter desktop app and are comfortable with some Rust.
The shared CLAUDE.md skeleton for app shells
This block is framework-neutral. It goes in your project's CLAUDE.md as-is, then you append the block for your framework from the sections further down.
# App shell rules
## Architecture
- One bridge layer for native access (a module, plugin wrapper or preload script)
- UI code calls the bridge; it never calls platform APIs or generated native code directly
- Generated native projects are regenerated, not hand-edited (unless the framework says otherwise)
## Secrets and configuration
- Everything compiled into the app is PUBLIC: env vars inlined at build time, config files, assets
- Secrets live on our server; the app calls our API, never a third-party API with a secret key
- Per-environment config (development, preview, production) comes from the build system
## Permissions
- Every device capability has its declaration BEFORE first use:
iOS usage description strings, Android manifest permissions, desktop capability files
- Request permissions at the moment of use, with a fallback when denied
- Never add a permission the app does not use
## Releases
- Know which changes need a new binary (native code, permissions, SDK upgrades)
and which can ship as an update (JS, Dart or web-asset-only changes, where supported)
- Release builds are signed in CI with credentials from the secret store, never from the repo
- Version and build numbers are bumped by the release script, never by hand
## Dependencies
- Install with the framework's version-aware installer where one exists
- Pin the framework version in this file; Claude checks it before generating API calls
## Hard rules
- NEVER put an API secret in client code or client-side env vars
- NEVER ship a native change through an over-the-air or web-asset update
- NEVER call a permission-gated API without its declaration
- NEVER commit signing keys, keystores or provisioning profiles
Each section of that block exists because of one of the six failure patterns above. The rest of this section explains the pieces that need more than a one-line rule.
Everything in the client is public
This is the rule Claude breaks most often, because environment variables feel private. Expo's documentation says plainly that EXPO_PUBLIC_ variables are inlined into the JavaScript bundle and are visible in plain text in the compiled app (Expo environment variables), and its EAS guide states that anything included in client-side code should be considered public. The same holds for Flutter build defines, Capacitor web bundles and Electron renderer code. The shared rule: the app only ever holds values you would be comfortable publishing, such as an API base URL or a publishable key, and it reaches anything privileged through your own server.
Native changes need a new binary
Expo, React Native and Capacitor all let you change the JavaScript or web layer without touching native code, and Expo can deliver those changes over the air. The catch is compatibility. Expo's runtime version exists to guarantee that an update matches the native code in a build, and any native change requires a new build before you publish an update (Expo runtime versions). Tell Claude which kinds of change require a binary and make it state, for every change it proposes, which path it needs:
## Change classification (state this for every change)
- Binary required: new native module or plugin, permission change, app config native fields,
framework or SDK upgrade, icon or splash change
- Update or web rebuild is enough: UI and logic changes in JS, TS or web code, copy changes,
new pure-JS dependencies
One bridge layer
Claude follows local patterns. If the first screen it sees calls a native plugin directly, it will do the same in the next twenty. A single wrapper per capability (src/native/camera.ts, a Flutter PlatformService, an Electron preload API) gives it one obvious thing to call, and keeps permission checks, error handling and platform differences in one place.
Expo rules for Claude Code
Expo is the framework most React teams use for React Native, and SDK 58 was released on 15 October 2026 (Expo changelog). Its rules are about choosing the right runtime, keeping native projects generated, and keeping updates compatible.
- Use development builds, not Expo Go, for real projects. Expo's workflow guide calls Expo Go a limited playground and recommends development builds, which include
expo-dev-clientand support any native library (Expo workflow overview). - Treat native projects as generated. With Continuous Native Generation,
npx expo prebuildgenerates theandroidandiosdirectories from your app config and package.json, and you can delete and regenerate them at any time. Native modifications belong in config plugins and app config, not in hand edits that the next prebuild discards. - Install with
npx expo install. It is a drop-in replacement fornpm installthat picks versions known to work with your project's React Native version.npx expo install --checkreports mismatches (and exits non-zero in CI),--fixupdates them (Expo CLI). - Reference public env vars statically.
EXPO_PUBLIC_variables are only inlined when written asprocess.env.EXPO_PUBLIC_NAME. Bracket access and destructuring are not inlined, so the value is undefined at runtime. - Keep build secrets in EAS environment variables. Variables are set with
eas env:setfor thedevelopment,previewandproductionenvironments, withplaintext,sensitiveorsecretvisibility;secretvalues are not readable outside EAS servers (EAS environment variables). - Pick a runtime version policy on purpose.
appVersionchanges the runtime version when you bump the app version, so native changes without a version bump cause mismatches;fingerprintchanges it whenever anything that could affect the native runtime changes, which needs more builds but makes incompatible updates very unlikely.
## Expo
- Expo SDK 58; development builds with expo-dev-client (Expo Go only for throwaway prototypes)
- android/ and ios/ are generated by npx expo prebuild; native changes go in app.config.ts + config plugins
- Install packages with npx expo install; CI runs npx expo install --check
- Client env: process.env.EXPO_PUBLIC_X written literally; never destructured or bracket-accessed
- Build-time secrets: eas env:set --visibility secret per environment
- runtimeVersion policy: fingerprint
- Every change states: EAS Update OK, or new build required
React Native rules for Claude Code
Plain React Native (version 0.88 on npm as of this writing) is what you use when you own the native projects. The New Architecture has been enabled by default in all React Native projects since 0.76, with an opt-out flag per platform for libraries that cannot use it yet (React Native architecture).
- Start new apps on a framework. React Native's documentation recommends a framework for new apps and gives
npx create-expo-app@latestas the starting command. If you are already on Expo, use the Expo block above; this block is for teams maintaining their ownios/andandroid/projects. - Write native modules for the New Architecture. Tell Claude the architecture is on, so it generates modules and components that use Codegen rather than legacy bridge patterns. If you must opt out, it is
newArchEnabled=falseinandroid/gradle.propertiesandRCT_NEW_ARCH_ENABLED=0in the Podfile, and CLAUDE.md should record why. - Pin the versions Claude must respect. React Native libraries change APIs between major versions (navigation, animation, styling). List the exact versions you use so Claude does not mix patterns from different majors.
- You own the native files here. Unlike an Expo prebuild project, edits under
ios/andandroid/are permanent. Require Claude to state every native file it touches and to rebuild both platforms before calling a native change done.
## React Native
- react-native 0.88; New Architecture ON (opt-out only with a documented reason)
- ios/ and android/ are hand-maintained: every native edit is listed in the change summary
- Native modules use Codegen specs; no legacy bridge modules in new code
- Pinned library majors: navigation, animation, styling (list exact versions here)
- A native change is done only after both iOS and Android builds succeed
Flutter rules for Claude Code
Flutter apps are written in Dart and render their own widgets, so most of the rules are about state and the platform boundary.
- Pick one state management approach and write it down. Flutter's own guidance does not mandate one approach: it presents
setStatefor widget-local ephemeral state,ValueNotifierandInheritedWidgetas the built-in options, and leaves packages to the app's complexity and the team's preference (Flutter state management options). That is exactly why Claude needs your choice in CLAUDE.md; without it, it will mix whichever pattern fits the current file. - Generate platform channels with Pigeon. A plain
MethodChannelis not type safe: both sides must agree on names and types by convention. Flutter's documentation recommends Pigeon, which generates type-safe Dart and host code from one Dart definition (Flutter platform channels). - Handle channel errors and threading. Channel calls are asynchronous and can throw
PlatformException(for example when an API is unavailable on a simulator), so every call needs a try/catch. Platform-side handlers run on the platform's main thread by default; heavy native work needs the Task Queue API on a background queue. - Keep platform code behind one interface. Scattered platform checks in feature code are where one-platform bugs hide. One service interface with one implementation per platform keeps the native surface testable.
## Flutter
- State: <your choice, e.g. Riverpod>; setState only for widget-local ephemeral state
- Platform channels generated with Pigeon from lib/platform/api.dart; no hand-written MethodChannel strings
- Every channel call wrapped in try/catch for PlatformException
- Heavy native handlers use a background task queue, never block the platform main thread
- Platform-specific code only in lib/platform/, behind one interface
- flutter analyze must report zero issues before a task is done
Capacitor rules for Claude Code
Capacitor puts a web app inside native iOS and Android shells. The current documentation is for Capacitor 8. Its rules are about keeping the web build and the native projects in sync and keeping development-only settings out of production.
- Sync after every web build and plugin change.
npx cap syncrunscopy(web assets into the native projects) and thenupdate(native plugin dependencies) (Capacitor sync). Make it the last step of every build script so Claude never ships stale assets. - Point
webDirat the real build output.webDiris the directory of compiled web assets and should contain the finalindex.html(Capacitor config). Bothcapacitor.config.tsandcapacitor.config.jsonare supported; pick one. - Keep
server.urlandallowNavigationout of production.server.urlloads an external URL and is intended for live-reload servers;allowNavigationis also documented as not intended for production. A release build that still points at a dev server is a common Claude mistake. - Leave
androidSchemeat its default. It defaults tohttps, and values other thanhttporhttpscan break routing. - Declare usage strings per plugin. The Camera plugin, for example, needs
NSCameraUsageDescription,NSPhotoLibraryUsageDescriptionandNSPhotoLibraryAddUsageDescriptionon iOS. On Android it needs no permissions unless you save to the gallery, in which case storage permissions apply withmaxSdkVersionlimits (Capacitor Camera). Tell Claude to read each plugin's install section before using it.
## Capacitor
- Capacitor 8; config in capacitor.config.ts only
- webDir = the framework's build output folder (contains index.html)
- Build script: web build, then npx cap sync (never skip)
- server.url and allowNavigation only in a dev-only config, never in release builds
- server.androidScheme left at default (https)
- Every plugin: Info.plist usage strings + Android permissions from its docs, added BEFORE first call
- Device APIs only through @capacitor/* or vetted community plugins, wrapped in src/native/
Swift rules for Claude Code
For Apple-only apps, the two areas where Claude needs the most guidance are the Xcode project file and Swift concurrency.
- Use buildable folders to keep the project file quiet. Xcode 16 introduced buildable folder references to "minimize project file changes, and avoid version control conflicts" (Xcode 16 release notes). Existing groups can be converted with Convert to Folder. With buildable folders, adding a Swift file does not require Claude to edit
project.pbxprojat all. - Never hand-edit
project.pbxproj. It is a generated graph that Xcode must be able to parse. If your project still needs project-file changes, route them through a generator such as XcodeGen or Tuist, or make them in Xcode, and tell Claude which. - Adopt the Swift 6 language mode deliberately. It is an opt-in mode that reports potential data races as compiler errors (Announcing Swift 6). State in CLAUDE.md which modules use it, so Claude fixes isolation errors with
@MainActor,Sendableand actors instead of silencing them. - Use Swift Testing for new tests. It ships with the toolchain, so
import Testingneeds no package dependency, and Xcode 16 addedswift testsupport for it. - Build every target before calling it done. A change that compiles for iOS can break a macOS or watchOS target. Make
xcodebuildfor each supported scheme part of the definition of done.
## Swift
- Swift 6 language mode in: <list modules>; isolation errors fixed, never suppressed
- Sources in buildable folders; project.pbxproj is never edited by hand
- Project-file changes (targets, build settings) go through <XcodeGen | Tuist | Xcode UI>
- UI state mutations on @MainActor
- New tests use Swift Testing (import Testing, @Test, #expect)
- SPM dependencies pinned to a version range, never a branch
- Done = xcodebuild succeeds for every supported scheme
Electron rules for Claude Code
Electron's secure defaults are good, and most Claude mistakes in Electron are attempts to switch them off. Electron 45.0.0 was published on npm on 21 October 2026; Electron ships a new major every 8 weeks and supports the latest three stable majors (Electron timelines), so staying current is a security task, not a nice-to-have.
- Keep the defaults on. Node.js integration has been off by default since Electron 5, context isolation on since 12, and renderer sandboxing on since 20 (Electron security checklist). Tell Claude that any change to
nodeIntegration,contextIsolationorsandboxis forbidden. - Use
invokeandhandlefor two-way IPC. Electron recommendsipcRenderer.invokepaired withipcMain.handlewhenever possible (Electron IPC). The preload script exposes narrow functions throughcontextBridge, neveripcRendereritself or itsonmethod. - Validate the sender and the payload. The security checklist says to validate the
senderof all IPC messages by default, because any frame can send one. Validate the payload too, especially before file system or shell operations. - Never pass untrusted input to
shell.openExternal. The checklist warns it can be used to compromise the user's machine. Allowlist protocols and hosts. - Define a Content Security Policy and review fuses. A restrictive CSP such as
script-src 'self'is on the checklist, as is using@electron/fusesto disable features you do not need. - Package with Electron Forge and sign. Electron's tutorial recommends Electron Forge for packaging and distribution, and notes that code signing is mandatory for its auto-update step (Electron packaging).
## Electron
- electron 45.x; upgrade within the supported window (latest three majors)
- webPreferences: never change nodeIntegration, contextIsolation or sandbox from their defaults
- IPC: ipcMain.handle + ipcRenderer.invoke; channel names in src/shared/ipc-channels.ts
- Preload exposes named functions via contextBridge; NEVER ipcRenderer or ipcRenderer.on directly
- Every handler validates the IPC sender and the payload schema
- shell.openExternal only for allowlisted https URLs
- CSP set for every window; fuses reviewed at each release
- Packaging: Electron Forge; signing credentials from CI secrets only
Tauri rules for Claude Code
Tauri 2 pairs a web frontend with a Rust backend and an explicit permission system. Most failed Claude sessions in Tauri come from three places: unregistered commands, missing permissions and mismatched argument names.
- Register every command in one
invoke_handler. Commands defined with#[tauri::command]are passed totauri::generate_handler!insideinvoke_handler, and only the lastinvoke_handlercall is used, so all commands go in a single call (Tauri calling Rust). - Argument names are camelCase on the JavaScript side. Rust
snake_caseparameters are exposed as camelCase by default; use#[tauri::command(rename_all = "snake_case")]if you want otherwise. Fields inside struct arguments follow the struct's serde attributes, so add#[serde(rename_all = "camelCase")]to structs the frontend builds. - Async commands take owned arguments. Borrowed arguments such as
&strdo not work in async command signatures; use owned types likeString. - Errors must serialize. Everything a command returns must implement
serde::Serialize, including errors. Map external errors withmap_err(|e| e.to_string())or define a serializable error type. - Grant permissions through capability files. Capabilities live as JSON or TOML in
src-tauri/capabilitiesand grant permissions such ascore:path:defaultorfs:defaultto named windows (Tauri capabilities). By default all registered app commands are callable from every window; restrict them withAppManifest::commandsinbuild.rswhen windows should differ. - Open URLs with the opener plugin. Its default permissions allow
mailto:,tel:,http://andhttps://URLs and revealing files in their folder; anything broader must be granted explicitly (Tauri opener). - Sidecars need a target triple and an explicit grant. Each binary in
bundle.externalBinmust exist with a-$TARGET_TRIPLEsuffix, and running it needs ashell:allow-execute(orshell:allow-spawn) permission with"sidecar": true(Tauri sidecars). - The updater requires signatures. Signature verification cannot be disabled. Generate keys with
tauri signer generate, setbundle.createUpdaterArtifacts, and provideTAURI_SIGNING_PRIVATE_KEYfrom the shell environment at build time, because.envfiles do not work for it (Tauri updater).
## Tauri
- Tauri 2 (@tauri-apps/api 2.x); Rust commands in src-tauri/src/commands/
- ONE invoke_handler(tauri::generate_handler![...]) listing every command
- JS passes camelCase args; structs from the frontend derive #[serde(rename_all = "camelCase")]
- Async commands take owned args (String, not &str); return Result<T, E> with E: Serialize
- Permissions only via src-tauri/capabilities/*.json, scoped to named windows; list what is open here
- URLs and file reveals: opener plugin with default permissions
- Sidecars: bundle.externalBin with -$TARGET_TRIPLE binaries + shell:allow-execute { sidecar: true }
- Updater: signed artifacts; TAURI_SIGNING_PRIVATE_KEY from CI env, never .env or the repo
Common Claude Code app mistakes, and the rule that prevents each
Paste the rows for your framework into CLAUDE.md under its block.
| What Claude writes by default | Why it hurts | Rule to add |
|---|---|---|
A secret key in EXPO_PUBLIC_* or another client env var |
It is readable in the shipped app | Secrets stay on the server; client gets public values only |
const { EXPO_PUBLIC_API_URL } = process.env |
Not inlined, so undefined at runtime | Reference process.env.EXPO_PUBLIC_X literally |
A native module shipped with eas update |
Installed builds lack the native code | Native changes need a new build; state the path for every change |
Hand edits inside a prebuild-generated ios/ or android/ |
Overwritten on the next prebuild | Config plugins and app config only |
npm install of an Expo-managed package |
Version may not match the SDK | npx expo install, --check in CI |
A hand-written MethodChannel with string method names |
No type safety between Dart and host | Pigeon-generated channels |
A Capacitor release still pointing server.url at a dev server |
The app loads a development URL | Dev-only config for server.url and allowNavigation |
| A camera call with no usage description | The plugin's required Info.plist keys are missing | Usage strings added before first call |
Edits to project.pbxproj |
Version control conflicts in a file Xcode must parse | Buildable folders or a project generator |
nodeIntegration: true to read a file |
Renderer gets full Node.js | Preload function plus ipcMain.handle |
contextBridge.exposeInMainWorld('ipc', ipcRenderer) |
Exposes the whole IPC system | Narrow named functions only |
A Tauri command missing from generate_handler! |
The frontend cannot call it | One handler list with every command |
| A Tauri feature with no capability entry | Blocked at runtime by the permission system | Capability file updated with the command |
Permission hooks for build and release scripts
App projects collect commands that cost money or reach users: cloud builds, store submissions, update publishes, signing-key generation. Let Claude run local checks freely and make it ask for anything that ships:
{
"permissions": {
"allow": [
"Bash(npx expo install --check*)",
"Bash(flutter analyze*)",
"Bash(npx cap sync*)"
],
"deny": [
"Bash(eas build*)",
"Bash(eas submit*)",
"Bash(eas update*)",
"Bash(npx tauri signer*)",
"Bash(npm run publish*)"
]
}
}
Put this in .claude/settings.local.json. Our guide to Claude Code permissions explains how allow and deny rules work in full.
Frequently asked questions
Can Claude Code build a mobile app?
Yes. Claude Code writes and runs the code, configuration and build commands for Expo, React Native, Flutter, Capacitor and Swift projects. What it cannot see is which values end up public in the shipped app and which changes need a new store build, so those rules belong in CLAUDE.md.
Which framework works best with Claude Code for mobile apps?
For React teams, Expo, because it generates the native projects from configuration and gives Claude fewer native files to get wrong; React Native's own documentation recommends a framework for new apps. Flutter suits Dart teams, Capacitor suits teams with an existing web app, and Swift suits Apple-only products.
Electron or Tauri with Claude Code?
Both work. Electron gives you full Node.js and a bundled Chromium, and its main risk is code that disables its secure defaults. Tauri gives you a Rust backend and an explicit capability system, and its main friction is registering commands and granting permissions. Pick on team skills and app size, then use the matching block above.
How do I stop Claude Code from putting secrets in my app?
State in CLAUDE.md that everything compiled into the app is public, keep secret keys on your own server, and keep build-time credentials in your build system's secret store (for Expo, EAS environment variables with secret visibility). Add a deny rule for any script that publishes builds so Claude has to ask first.
What happened to the separate Expo, React Native, Flutter, Capacitor, Swift, Electron and Tauri guides?
They are merged into this page. One shared skeleton plus a short block per framework is easier to copy and easier to keep current, and every framework fact here was checked against current documentation on 21 October 2026.
Related guides
- Claude Code with Tamagui: universal UI components for React Native and web, kept as a separate deep dive.
- Claude Code with Zustand and Claude Code with React Query: state and server-data patterns that carry over to React Native and Electron renderers.
- Claude Code for background jobs and queues: the server side that mobile and desktop apps hand long work to.
- Claude Code for transactional email: the same one-skeleton approach for Resend, Postmark, SendGrid, Mailgun and Mailtrap.
Build apps that respect the native boundary
The pattern here is the same one that makes Claude Code reliable anywhere: decide the non-negotiable rules once, write them where Claude reads them every session, and keep platform access behind one bridge. For app shells, that means public-only client config, a clear line between updates and new builds, permissions declared before use, generated projects left to their generators, and desktop renderers with no more power than they need.
Get Claudify. A complete Claude Code configuration layer with 1,738 skills, 13 specialist agents and persistent memory, ready to adapt to your stack.
More like this
Ready to upgrade your Claude Code setup?
Get Claudify