Install @bittingz/expo-widgets
mainInstall the package using npx expo install.
- Use v1 packages for Expo 49.
- Use v2 packages for Expo 51+.
npx expo install @bittingz/expo-widgetsrepository·main·Indexed 43 days ago
https://github.com/mike-stewart-dev/expo-widgetsAn Expo module and Config Plugin that enables the creation and management of native widgets for iOS and Android. It provides automated Xcode target creation for iOS, Kotlin support for Android, and tools for managing App Group permissions and native source files within an Expo project. Supports version 3.0.1, with v1 for Expo 49 and v2 for Expo 51+.
Install the package using npx expo install.
npx expo install @bittingz/expo-widgetsTo use custom fonts in iOS widgets, use the expo-native-fonts package.
Important: You must place the expo-native-fonts configuration in your plugins array before the @bittingz/expo-widgets configuration.
Within your designated iOS widget source folder (ios.src), you must create the following files:
Module.swiftAssets.xcassetsIf you have Swift files (like data models) used by both the main module and the widget, add them to the moduleDependencies array in your plugin configuration to ensure they are included.
The Android source folder (android.src) must mimic a standard Android Studio project structure. It requires two main subfolder paths:
/android/main/java/package_name: Place your .kt (Kotlin) widget files here./android/res/: Place your assets here (the same as in Android Studio).Configuration requirements:
widgets array: Define your providers. The resourceName must point to your XML file in the res folder (e.g., @xml/my_widget_info for /res/xml/my_widget_info.xml).distPlaceholder: For apps requiring multiple distributions with different package names, set this to a string (e.g., com.company.app). The plugin will replace all instances of this placeholder in your widget source files with the actual package name from your config during build.To enable native widgets, add @bittingz/expo-widgets to your plugins array in app.config.{js/ts} or app.json. You can configure iOS and Android separately. If you only support one platform, you can omit the other.
[
"@bittingz/expo-widgets",
{
ios: {
src: "./src/my/path/to/ios/widgets/folder",
devTeamId: "your apple dev team ID",
mode: "production",
moduleDependencies: [],
useLiveActivities: false,
frequentUpdates: false,
entitlements: {
"any xcode entitlement the widget needs": "entitlement value"
}
},
android: {
src: "./src/my/path/to/android/widgets/folder",
widgets: [
{
"name": "MyWidgetProvider",
"resourceName": "@xml/my_widget_info"
}
],
distPlaceholder: "optional.placeholder"
}
}
],You can customize Xcode build settings by adding an xcode object to your iOS configuration in app.json or app.config.js. All properties are optional.
appExtAPI: A boolean that sets APP_EXTENSION_API_ONLY in the Podfile.configOverrides: An object of key-value pairs representing xcodeproj values. Keys must match the case exactly (e.g., SWIFT_VERSION: '5.0').entitlements: An object of key-value pairs for widget entitlements."@bittingz/expo-widgets",
{
"ios": {
"xcode": {
"appExtAPI": true,
"configOverrides": {
"SWIFT_VERSION": "5.0"
},
"entitlements": {
"com.apple.developer.widget-extension": true
}
}
}
}If you use R in your Kotlin widget files to access layouts and encounter an unresolved reference R error:
package your.appconfig.packageid.R.android folder.When using expo-widgets in a monorepo structure (where the package resides in a parent directory relative to the Expo project), you must configure metro.config.js to prevent dependency conflicts and ensure the bundler can locate the package and its dependencies.
Key configuration steps:
config.resolver.blockList to prevent Metro from bundling react or react-native from the parent node_modules folder, which avoids version mismatch errors../node_modules and the parent ../node_modules in config.resolver.nodeModulesPaths.config.resolver.extraNodeModules to map the package name @bittingz/expo-widgets to the parent directory ...config.watchFolders so Metro tracks changes in the package source.const { getDefaultConfig } = require('expo/metro-config');
const path = require('path');
const config = getDefaultConfig(__dirname);
// Prevent incompatible react/react-native versions from parent folder
config.resolver.blockList = [
...Array.from(config.resolver.blockList ?? []),
new RegExp(path.resolve('..', 'node_modules', 'react')),
new RegExp(path.resolve('..', 'node_modules', 'react-native')),
];
// Ensure Metro looks in both local and parent node_modules
config.resolver.nodeModulesPaths = [
path.resolve(__dirname, './node_modules'),
path.resolve(__dirname, '../node_modules'),
];
// Map the package to the parent directory
config.resolver.extraNodeModules = {
'@bittingz/expo-widgets': '..',
};
// Watch the parent directory for changes
config.watchFolders = [path.resolve(__dirname, '..')];
module.exports = config;The expo-module.config.json file defines the native module mappings for the expo-widgets package. It specifies which native classes are exposed to the Expo runtime on iOS and Android.
ExpoWidgetsModule class.expo.modules.widgets.ExpoWidgetsModule class.{
"platforms": ["ios", "android"],
"ios": {
"modules": ["ExpoWidgetsModule"]
},
"android": {
"modules": ["expo.modules.widgets.ExpoWidgetsModule"]
}
}When configuring iOS widgets, provide the following properties:
src: The directory containing your widget native code.deploymentTarget: The deployment target for the app (defaults to 16.2 to support live activities).useLiveActivities: Boolean to enable live activities (SSupportsLiveActivities).frequentUpdates: Boolean to enable frequent live activity updates (NSSupportsLiveActivitiesFrequentUpdates).devTeamId: Your Apple developer team ID.targetName: (Optional) The target/extension name.xcode: (Optional) An object to override Xcode project settings:widgetBundleIdentifier: The bundle ID for the widget(s).appGroupId: Optional app group ID override.entitlements: Optional IOSEntitlements (a PlistObject).configOverrides: A dictionary of attributeName: string to customize the Xcode configuration block.appExtAPI: Boolean to control the Podfile APP_EXTENSION_API_ONLY setting.moduleDependencies: (Optional) A collection of relative file paths to files your module needs (e.g., shared models).widgetExtPlugins: (Optional) An array of ConfigPlugins to run after the widget extension is created.type WithExpoIOSWidgetsProps = {
src: string;
deploymentTarget: string;
useLiveActivities: boolean;
frequentUpdates: boolean;
devTeamId: string;
targetName?: string;
xcode?: {
widgetBundleIdentifier?: string;
appGroupId?: string;
entitlements?: IOSEntitlements;
configOverrides?: { [ attributeName: string ]: string };
appExtAPI?: boolean;
};
moduleDependencies: string[];
widgetExtPlugins: ConfigPlugin<any>[];
}Use the withGsonGradle ConfigPlugin to automatically add the Gson dependency to your Android app's build.gradle file. It injects implementation("com.google.code.gson:gson:2.10.1") into the dependencies { ... } block.
import { withGsonGradle } from '@bittingz/expo-widgets';
// In your app.config.js or app.json
export default {
expo: {
plugins: ['withGsonGradle'],
},
};Use the withEntitlements config plugin to manage iOS widget entitlements, specifically for setting up App Groups and Push Notification environments. This plugin automatically creates a .entitlements file within your widget project directory.
To use this, you must provide options containing the mode ('development' or 'production') and optionally custom xcode.entitlements.
Requirements:
appGroupId either via options.xcode.appGroupId or by ensuring ios.bundleIdentifier is set in your Expo config (the plugin will attempt to generate one using the pattern group.${bundleIdentifier}.expowidgets).ios.bundleIdentifier is missing and no appGroupId is provided, the plugin will throw an error.// Example usage within an Expo Config Plugin context
withEntitlements(config, {
mode: 'development',
xcode: {
appGroupId: 'group.com.your.app.id',
entitlements: {
// custom entitlements here
}
}
});