The mod system provides controlled file modification capabilities for native project files. It handles file reading/writing, lifecycle management, introspection mode, and provides a structured way to modify Android and iOS project files through typed interfaces.
Apply a modifier to a specific platform and mod type.
function withMod<T>(
config: ExportedConfig,
options: {
platform: ModPlatform;
mod: string;
action: Mod<T>;
}
): ExportedConfig;Parameters:
config: The Expo configuration objectoptions.platform: Target platform ('android' | 'ios')options.mod: Name of the mod to apply (e.g., 'manifest', 'infoPlist')options.action: Modification function to executeUsage:
config = withMod(config, {
platform: "android",
mod: "manifest",
action: async (config) => {
const manifest = config.modResults;
// Modify manifest
return config;
}
});Apply a base mod with provider functionality for file access.
function withBaseMod<T>(
config: ExpoConfig,
options: {
platform: ModPlatform;
mod: string;
action: Mod<T>;
isProvider?: boolean;
isIntrospective?: boolean;
}
): ExpoConfig;Parameters:
config: The Expo configuration objectoptions.platform: Target platformoptions.mod: Mod nameoptions.action: Modification functionoptions.isProvider: Whether this mod provides data to other modsoptions.isIntrospective: Whether this mod supports introspection modeApply a dangerous mod that runs before all other platform mods.
function withDangerousMod(
config: ExpoConfig,
platform: ModPlatform,
action: Mod<unknown>
): ExpoConfig;Usage:
config = withDangerousMod(config, "android", async (config) => {
// Dangerous operations before other mods
return config;
});Apply a finalized mod that runs after all other platform mods.
function withFinalizedMod(
config: ExpoConfig,
platform: ModPlatform,
action: Mod<unknown>
): ExpoConfig;Compile and execute all mods for the given configuration.
function compileModsAsync(
config: ExportedConfigWithProps,
options: {
projectRoot: string;
platforms: ModPlatform[];
introspect?: boolean;
assertMissingModProviders?: boolean;
}
): Promise<ExportedConfigWithProps>;Parameters:
config: Configuration with mods to compileoptions.projectRoot: Root directory of the projectoptions.platforms: Platforms to compile mods foroptions.introspect: Whether to run in introspection modeoptions.assertMissingModProviders: Whether to assert on missing providersEvaluate mods in introspection mode without making file system changes.
function evalModsAsync(
config: ExportedConfigWithProps,
options: {
projectRoot: string;
platforms: ModPlatform[];
introspect: true;
}
): Promise<ExportedConfigWithProps>;Apply default base mods for specified platforms.
function withDefaultBaseMods(
config: ExpoConfig,
options?: {
platforms?: ModPlatform[];
skipEmptyMods?: boolean;
}
): ExpoConfig;Object containing base mod utilities and providers.
const BaseMods: {
withGeneratedBaseMods: typeof withGeneratedBaseMods;
provider: typeof provider;
withAndroidBaseMods: typeof withAndroidBaseMods;
getAndroidModFileProviders: typeof getAndroidModFileProviders;
withIosBaseMods: typeof withIosBaseMods;
getIosModFileProviders: typeof getIosModFileProviders;
};type Mod<Props = any> = ((
config: ExportedConfigWithProps<Props>
) => OptionalPromise<ExportedConfigWithProps<Props>>) & {
isProvider?: boolean;
isIntrospective?: boolean;
};
interface ModProps<T = any> {
readonly projectRoot: string;
readonly platformProjectRoot: string;
readonly modName: string;
readonly platform: ModPlatform;
readonly introspect: boolean;
readonly projectName?: string;
readonly ignoreExistingNativeFiles?: boolean;
nextMod?: Mod<T>;
}
interface ExportedConfigWithProps<Data = any> extends ExpoConfig {
modResults: Data;
modRequest: ModProps<Data>;
readonly modRawConfig: ExpoConfig;
mods?: ModConfig | null;
}
interface ModConfig {
android?: AndroidModConfig;
ios?: IOSModConfig;
}
interface AndroidModConfig {
dangerous?: Mod<unknown>;
finalized?: Mod<unknown>;
manifest?: Mod<AndroidManifest>;
strings?: Mod<ResourceXML>;
colors?: Mod<ResourceXML>;
colorsNight?: Mod<ResourceXML>;
styles?: Mod<ResourceXML>;
mainActivity?: Mod<ApplicationProjectFile>;
mainApplication?: Mod<ApplicationProjectFile>;
appBuildGradle?: Mod<GradleProjectFile>;
projectBuildGradle?: Mod<GradleProjectFile>;
settingsGradle?: Mod<GradleProjectFile>;
gradleProperties?: Mod<Properties.PropertiesItem[]>;
}
interface IOSModConfig {
dangerous?: Mod<unknown>;
finalized?: Mod<unknown>;
infoPlist?: Mod<InfoPlist>;
entitlements?: Mod<Plist>;
expoPlist?: Mod<Plist>;
xcodeproj?: Mod<XcodeProject>;
appDelegate?: Mod<AppDelegateProjectFile>;
podfileProperties?: Mod<Record<string, string>>;
}import { withMod } from "@expo/config-plugins";
const withCustomMod = (config) => {
return withMod(config, "android", "manifest", async (config) => {
const manifest = config.modResults;
// Modify the Android manifest
manifest.application[0].$["android:theme"] = "@style/CustomTheme";
return config;
});
};const withIntrospectiveMod = (config) => {
const introspectiveMod: Mod<InfoPlist> = async (config) => {
if (config.modRequest.introspect) {
// Only read/analyze, don't modify
console.log("Current bundle ID:", config.modResults.CFBundleIdentifier);
return config;
}
// Normal modification
config.modResults.CFBundleIdentifier = "com.example.newid";
return config;
};
introspectiveMod.isIntrospective = true;
return withMod(config, "ios", "infoPlist", introspectiveMod);
};const withProviderMod = (config) => {
const providerMod: Mod<any> = async (config) => {
// Provide data to downstream mods
config.modResults = {
customData: "shared-value",
timestamp: Date.now()
};
return config;
};
providerMod.isProvider = true;
return withBaseMod(config, {
platform: "android",
mod: "customProvider",
action: providerMod,
isProvider: true
});
};