什么是配置插件以及何时使用
了解 Expo 的预构建流程如何将 app.json 转换为原生 iOS 和 Android 项目,并确定何时需要配置插件来添加第三方原生库。
什么是配置插件以及何时使用 是 CoddyKit 上的免费 React Native Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Native Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Native Academy 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Expo Managed Workflow and Native Files
In the Expo managed workflow, you never manually edit the ios/ or android/ folders. Instead, Expo's prebuild pipeline generates those native projects from app.json and your JavaScript dependencies. This is powerful for keeping native files in sync, but it creates a challenge: how do you customize native settings for third-party libraries that require specific native code changes?
What Is a Config Plugin
A Config Plugin is a JavaScript function that receives the current Expo app configuration object and returns a modified version of it. Expo's prebuild step applies all config plugins in sequence before generating native iOS and Android projects. Config plugins let you add permissions, modify build settings, add native files, and configure manifests — all from JavaScript, without touching native code directly.
// Minimal config plugin shape
const { withInfoPlist } = require('@expo/config-plugins');
module.exports = function withMyPlugin(config) {
// Receive config, return modified config
return withInfoPlist(config, (iosConfig) => {
iosConfig.modResults['NSCameraUsageDescription'] =
'We need camera access to scan QR codes.';
return iosConfig;
});
};When Do You Need a Config Plugin
You need a config plugin when a native library requires changes that app.json alone cannot express. Common examples include: adding a permission string to Info.plist or AndroidManifest, adding a service or receiver declaration, modifying build.gradle dependencies, copying native asset files into the iOS or Android project, or setting custom URL schemes for OAuth. If a library's README says 'Add this to Info.plist', you need a plugin.
// app.json — referencing plugins
{
'expo': {
'name': 'MyApp',
'plugins': [
'expo-camera',
'expo-location',
['./plugins/withStripePlugin', { 'merchantId': 'merchant.com.myapp' }],
'@react-native-google-signin/google-signin'
]
}
}The Prebuild Pipeline Step by Step
When you run npx expo prebuild, Expo performs these steps: (1) reads app.json, (2) resolves all plugins listed in the plugins array, (3) applies each plugin's transform function to the config in order, (4) writes the final ios/ and android/ directories based on the transformed config. The generated native files replace the previous ones — meaning manual edits to native files are overwritten on every prebuild.
# Run prebuild (regenerates ios/ and android/)
npx expo prebuild
# Prebuild for a specific platform
npx expo prebuild --platform ios
npx expo prebuild --platform android
# Clean existing native dirs first (fresh generation)
npx expo prebuild --cleanThe @expo/config-plugins Package
Expo provides the @expo/config-plugins package with a set of helper functions called mods (modifiers). Each mod targets a specific native file: withInfoPlist for iOS Info.plist, withAndroidManifest for AndroidManifest.xml, withGradleProperties for Android properties, withXcodeProject for the Xcode project file, and more. You compose mods to build up complex native configurations.
const {
withInfoPlist,
withAndroidManifest,
withGradleProperties,
withEntitlementsPlist,
} = require('@expo/config-plugins');
// Each mod receives (config, modifier function)
// The modifier receives the parsed native file, modifies it, returns it
module.exports = function withAllPlatforms(config) {
config = withInfoPlist(config, (c) => { /* iOS */ return c; });
config = withAndroidManifest(config, (c) => { /* Android */ return c; });
return config;
};Plugin Composition with withPlugins
If your plugin needs to apply multiple mods, use withPlugins to compose them cleanly. Pass it an array of [pluginFunction, options] tuples and it applies them in sequence. This makes complex plugins readable and avoids deeply nested function calls. Large config plugins (like one for a full payment SDK) often use withPlugins internally to organize their multiple native file modifications.
const { withPlugins } = require('@expo/config-plugins');
const { withCameraPermission } = require('./withCameraPermission');
const { withGoogleServicesFile } = require('./withGoogleServicesFile');
const { withFirebaseAndroid } = require('./withFirebaseAndroid');
module.exports = function withFirebasePlugin(config, options) {
return withPlugins(config, [
[withCameraPermission, { text: options.cameraPermissionText }],
withGoogleServicesFile,
withFirebaseAndroid,
]);
};Plugins Bundled with Libraries
Many popular React Native libraries ship their own config plugin as the app.plugin.js file at their package root. When you add such a library to the plugins array in app.json, Expo auto-discovers and runs this file during prebuild. Libraries like expo-camera, expo-notifications, react-native-maps, and @stripe/stripe-react-native all use this approach.
// node_modules/expo-camera/app.plugin.js (simplified)
const { withPermissions } = require('@expo/config-plugins');
module.exports = function withCamera(config) {
return withPermissions(config, [
'android.permission.CAMERA',
'android.permission.RECORD_AUDIO',
]);
};
// app.json just needs:
// 'plugins': ['expo-camera']Custom Plugin vs Bare Workflow
Before writing a custom config plugin, ask: could I switch to a bare workflow instead? In a bare workflow you eject from Expo managed and edit native files directly. Config plugins are better when you want to keep the managed workflow (easier OTA updates, no native IDE required, cloud builds). If you frequently need deep native customization, a bare workflow (or Expo's hybrid bare + config-plugins approach) may be more practical.
Inspecting Prebuild Output
After running expo prebuild, open the generated ios/ and android/ folders to verify your plugin worked. Check ios/MyApp/Info.plist for iOS changes and android/app/src/main/AndroidManifest.xml for Android changes. You can also run expo prebuild --no-install to skip pod install and inspect quickly. Add assertions in your plugin that throw if a required key is missing to catch misconfigurations early.
# After prebuild, check AndroidManifest
grep -A2 'CAMERA' android/app/src/main/AndroidManifest.xml
# Expected: <uses-permission android:name='android.permission.CAMERA'/>
# Check Info.plist on iOS
grep -A1 'NSCamera' ios/MyApp/Info.plist
# Expected:
# <key>NSCameraUsageDescription</key>
# <string>We need camera access...</string>Config Plugin Error Handling
Config plugins run at build time, so errors appear during expo prebuild rather than at runtime. If a plugin throws, prebuild fails with a descriptive stack trace showing which plugin and which step failed. Always validate your plugin inputs (e.g., check that a required API key option was provided) and throw with a clear error message so developers know exactly what to fix in their app.json.
module.exports = function withStripe(config, options = {}) {
if (!options.merchantId) {
throw new Error(
'withStripe: You must provide a merchantId option in app.json. ' +
'Example: ["./plugins/withStripe", { "merchantId": "merchant.com.yourapp" }]'
);
}
return withInfoPlist(config, (c) => {
c.modResults['StripePublishableKey'] = options.publishableKey;
return c;
});
};Config Plugins vs app.json Fields
Many common native settings are already supported directly in app.json without needing a plugin — things like icon, splash, permissions (for Expo bare), backgroundColor, and orientation. Only reach for a config plugin when you need to modify something that app.json has no built-in field for. Check the Expo app.json reference first before writing a custom plugin to avoid reinventing existing functionality.
// app.json built-in fields (no plugin needed)
{
'expo': {
'icon': './assets/icon.png',
'splash': { 'image': './assets/splash.png' },
'ios': {
'bundleIdentifier': 'com.yourapp',
'infoPlist': {
'NSPhotoLibraryUsageDescription': 'Save your photos'
// ^ built-in way to set Info.plist keys
}
}
}
}Quick Check
Test your understanding of React Native Mobile Development concepts from this lesson.
Lesson Recap
In this lesson you learned: why config plugins exist as the managed-workflow alternative to editing native files, how Expo's prebuild pipeline applies plugins to generate native projects, and how libraries ship their own app.plugin.js files for automatic native configuration. You also saw how to use @expo/config-plugins helpers like withInfoPlist and withAndroidManifest. Next up we write our first config plugin from scratch.
常见问题解答
「什么是配置插件以及何时使用」课时是免费的吗?
是的 — 「什么是配置插件以及何时使用」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Native Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Native Academy 课程共包含 4 节课。
「什么是配置插件以及何时使用」这节课中我会学到什么?
了解 Expo 的预构建流程如何将 app.json 转换为原生 iOS 和 Android 项目,并确定何时需要配置插件来添加第三方原生库。 你通过在浏览器中直接运行的动手代码来练习 React Native Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Native Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Native Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「什么是配置插件以及何时使用」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Native Academy 课中编写并运行代码吗?
能。每节 React Native Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。