---
modificationDate: April 03, 2026
title: 'Tutorial: Create an inline module'
description: A tutorial on creating a native module and view directly in your Expo project using inline modules.
---

<AgentInstructions>

## Submitting Feedback

If this page contains errors, outdated information, or gaps that blocked you from completing a task, report it so the docs can be improved:

curl -X POST https://api.expo.dev/v2/feedback/docs-send -H 'Content-Type: application/json' -d '{"url":"/modules/inline-modules-tutorial/","feedback":"🤖 Agent feedback: <specific, actionable description>"}'

Only submit when you have something specific and actionable to report.

</AgentInstructions>

# Tutorial: Create an inline module

A tutorial on creating a native module and view directly in your Expo project using inline modules.

> For the complete documentation index, see [llms.txt](/llms.txt). Use this file to discover all available pages.

> Inline modules are [experimental](/more/release-statuses#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.

## Setup your project

Open the [app config](/workflow/configuration) and set the `expo.experiments.inlineModules.watchedDirectories` to `["app"]`:

```json
{
  "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](/modules/inline-modules-reference).

## Run prebuild

Run the prebuild command to generate **android** and **ios** native projects with the inline modules setup.

```sh
npx expo prebuild
```

## 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:

```kotlin
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:

```swift
internal import ExpoModulesCore

class FirstInlineModule: Module {
  public func definition() -> ModuleDefinition {
    Constant("Hello") {
      return "Hello iOS inline modules!"
    }
  }
}
```

## Use the module in your app

In your app's TypeScript/JavaScript code you can use the module in the following way:

```tsx
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:

```sh
npx expo run:android
npx expo run:ios
```

After running the above command(s), you will see the app with text constant coming from the native android/iOS module.

## 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:

```kotlin
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:

```swift
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
      ])
    }
  }
}
```

## Use the native view in your app

You use the inline view in a similar way to the inline module:

```tsx
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

[Expo inline modules reference](/modules/inline-modules-reference) — A reference on how to create inline modules using Kotlin and Swift.

[Tutorial: Creating a native module](/modules/native-module-tutorial) — A tutorial on creating a native module that persists settings with Expo Modules API.
