Tutorial: Create an inline module
Edit page
A tutorial on creating a native module and view directly in your Expo project using inline modules.
Warning: Inline modules are currently experimental. The API is subject to breaking changes.
In this tutorial, you will create an example native module and a native view directly inside your Expo project's app directory using inline modules.
Unlike the standard Expo Modules, inline modules don't require a separate package or create-expo-module scaffolding.
You write Kotlin and Swift files alongside your app code, and Expo discovers them automatically.
1
Setup your project
Open the app config and set the expo.experiments.inlineModules.watchedDirectories to ["app"]:
{ "expo": { "experiments": { "inlineModules": { "watchedDirectories": ["app"] } } } }
Defining expo.experiments.inlineModules enables inline modules functionalities in an Expo project.
In expo.experiments.inlineModules.watchedDirectories you can specify in which directories your inline modules live.
Note that not all directories are allowed. For more information, see the inline modules reference.
2
3
Create an inline module
For Android, create a Kotlin file called FirstInlineModule.kt inside the app directory and add a module similar to the following:
package app import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition class FirstInlineModule : Module() { override fun definition() = ModuleDefinition { Constant("Hello") { -> "Hello Android inline modules!" } } }
For iOS, create a Swift file called FirstInlineModule.swift inside the app directory:
internal import ExpoModulesCore class FirstInlineModule: Module { public func definition() -> ModuleDefinition { Constant("Hello") { return "Hello iOS inline modules!" } } }
4
Use the module in your app
In your app's TypeScript/JavaScript code you can use the module in the following way:
import { requireNativeModule } from 'expo'; import { Text } from 'react-native'; const FirstInlineModule = requireNativeModule('FirstInlineModule'); export default function InlineModulesDemoComponent() { return <Text> {FirstInlineModule.Hello} </Text>; }
Now, you can run the example app:
# Run the example app on Android-Â npx expo run:android# Run the example app on iOS-Â npx expo run:iosAfter running the above command(s), you will see the app with text constant coming from the native android/iOS module.
5
Create a native view
To create a native view inside the app directory, let's use the ExpoWebView example.
For Android, create a Kotlin file called FirstInlineView.kt inside the app directory and add a view similar to the following:
package app import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition import java.net.URL import android.content.Context import android.webkit.WebView import android.webkit.WebViewClient import expo.modules.kotlin.AppContext import expo.modules.kotlin.viewevent.EventDispatcher import expo.modules.kotlin.views.ExpoView class FirstInlineView : Module() { override fun definition() = ModuleDefinition { View(ExpoWebView::class) { Events("onLoad") Prop("url") { view: ExpoWebView, url: URL? -> view.webView.loadUrl(url.toString()) } } } } class ExpoWebView(context: Context, appContext: AppContext) : ExpoView(context, appContext) { private val onLoad by EventDispatcher() internal val webView = WebView(context).also { it.layoutParams = LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ) it.webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView, url: String) { onLoad(mapOf("url" to url)) } } addView(it) } }
For iOS, create a Swift file called FirstInlineView.swift inside the app directory:
internal import ExpoModulesCore import WebKit class FirstInlineView: Module { public func definition() -> ModuleDefinition { View(ExpoWebView.self) { Events("onLoad") Prop("url") { (view, url: URL) in if view.webView.url != url { let urlRequest = URLRequest(url: url) view.webView.load(urlRequest) } } } } } class ExpoWebView: ExpoView, WKNavigationDelegate { let webView = WKWebView() let onLoad = EventDispatcher() required init(appContext: AppContext? = nil) { super.init(appContext: appContext) clipsToBounds = true webView.navigationDelegate = self addSubview(webView) } override func layoutSubviews() { webView.frame = bounds } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { if let url = webView.url { onLoad([ "url": url.absoluteString ]) } } }
6
Use the native view in your app
You use the inline view in a similar way to the inline module:
import { requireNativeModule, requireNativeView } from 'expo'; import { StyleSheet, Text, View } from 'react-native'; const FirstInlineModule = requireNativeModule('FirstInlineModule'); const FirstInlineView = requireNativeView('FirstInlineView'); export default function InlineModulesDemoComponent() { return ( <> <View style={styles.textBox}> <Text style={styles.text}> {FirstInlineModule.Hello} </Text> </View> <FirstInlineView style={styles.inlineView} url="https://docs.expo.dev/modules/" /> </> ); } const styles = StyleSheet.create({ textBox: { height: 100, justifyContent: 'flex-end', alignItems: 'center' }, text: { fontSize: 26 }, inlineView: { flex: 1 }, });
Now run your example app by compiling it using npx expo run:android or npx expo run:ios command.
After running the above command(s), you will see the app with a text constant coming from the native android/iOS module and a web view coming from a native view.
Congratulations! You've created your first Expo inline module and view.
Next steps
A reference on how to create inline modules using Kotlin and Swift.
A tutorial on creating a native module that persists settings with Expo Modules API.