All skills
expo avatar

/expo-module

@5c8f62e official
by expoexpo/skills2.6k stars
156

Framework (OSS). Guide for creating and writing Expo native modules and views using the Expo Modules API (Swift, Kotlin, TypeScript). Covers module definition DSL, native views, shared objects, config plugins, lifecycle hooks, autolinking, and type system. Use when building or modifying native modules for Expo. Not for migrating an existing Swift module from the definition DSL to the Expo Modules API 2.0 macros; use expo-migrate-module (from the expo-experiments plugin) for that.

Use this Skill: https://skilld.dev/gh/expo/skills/expo-module

This session only. Nothing lands on disk.

referencesnative-view.md

≈898 tokens on demand. Your agent reads this file only when SKILL.md points to it.

Native View Reference

Native views let you render platform UI components (UIView on iOS, Android View on Android) as React components.

Defining a View

Swift:

public class MyViewModule: Module {
  public func definition() -> ModuleDefinition {
    Name("MyView")

    View(MyNativeView.self) {
      Prop("title") { (view: MyNativeView, title: String) in
        view.titleLabel.text = title
      }

      Events("onPress", "onLoad")

      AsyncFunction("reset") { (view: MyNativeView) in
        view.reset()
      }
    }
  }
}

class MyNativeView: ExpoView {
  let titleLabel = UILabel()

  required init(appContext: AppContext) {
    super.init(appContext: appContext)
    clipsToBounds = true
    addSubview(titleLabel)
  }

  override func layoutSubviews() {
    super.layoutSubviews()
    titleLabel.frame = bounds
  }
}

Kotlin:

class MyViewModule : Module() {
  override fun definition() = ModuleDefinition {
    Name("MyView")

    View(MyNativeView::class) {
      Prop("title") { view: MyNativeView, title: String ->
        view.titleView.text = title
      }

      Events("onPress", "onLoad")

      AsyncFunction("reset") { view: MyNativeView ->
        view.reset()
      }
    }
  }
}

class MyNativeView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
  val titleView = TextView(context).also {
    addView(it, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
  }
}

TypeScript:

import { requireNativeView } from "expo";

export type MyViewProps = {
  title?: string;
  onPress?: (event: { nativeEvent: { x: number; y: number } }) => void;
  onLoad?: () => void;
} & ViewProps;

const NativeView = requireNativeView<MyViewProps>("MyView");

export function MyView(props: MyViewProps) {
  return <NativeView {...props} />;
}

View Event Dispatching

Swift:

class MyNativeView: ExpoView {
  let onPress = EventDispatcher()

  func handleTap(at point: CGPoint) {
    onPress(["x": point.x, "y": point.y])
  }
}

Kotlin:

class MyNativeView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
  private val onPress by EventDispatcher()

  fun handleTap(x: Float, y: Float) {
    onPress(mapOf("x" to x, "y" to y))
  }
}

View Lifecycle

// Called after all props have been set
OnViewDidUpdateProps { (view: MyNativeView) in
  view.applyChanges()
}
// Android only - called when view is no longer used
OnViewDestroys { view: MyNativeView ->
  view.cleanup()
}

AsyncFunction on Views

Functions defined inside View are accessible via React ref:

const ref = useRef<MyView>(null);
// Call native function
await ref.current?.reset();

PropGroup (Android)

Batch-register multiple props with shared setter logic:

View(MyNativeView::class) {
  PropGroup("border", "width" to Float::class, "color" to Int::class) { view, index, value ->
    when (index) {
      0 -> view.borderWidth = value as Float
      1 -> view.borderColor = value as Int
    }
  }
}

GroupView (Android)

Enable view group functionality for managing child views:

View(MyContainerView::class) {
  GroupView {
    AddChildView { parent, child, index -> parent.addView(child, index) }
    GetChildCount { parent -> parent.childCount }
    GetChildViewAt { parent, index -> parent.getChildAt(index) }
    RemoveChildView { parent, child -> parent.removeView(child) }
    RemoveChildViewAt { parent, index -> parent.removeViewAt(index) }
  }
}

Source: SKILL.md on GitHub

No alerts17d4 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    The skill provides developer documentation and reference guides for creating and writing Expo native modules and views. All code examples and scripts align with legitimate development practices, and no security risks were identified.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 5c8f62e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 2 months ago
version
1.0.0
  • TypeScript
  • expo
  • native-modules
  • swift
  • kotlin
  • react-native
  • ios
  • android
  • config-plugins

README badge

README badge for expo/skills/expo-module

Guides you through creating and modifying Expo native modules in Swift (iOS) and Kotlin (Android), with TypeScript bindings. Use this skill when building native modules, native views, config plugins, or wrapping platform SDKs for React Native consumption.

Generated from the current SKILL.md.

What is the difference between a local module and a standalone module?
Local modules are for a single app and live in expo.autolinking.nativeModulesDir or modules/ folder, using the host app's tooling. Standalone modules have their own package metadata, scripts, and example app, and are intended for reuse, monorepos, or publishing.
Should I manually create native files or use create-expo-module?
Use create-expo-module to scaffold. It sets up the expected layout, expo-module.config.json, podspec/Gradle files, TypeScript bindings, and the example app flow. Manually creating files is error-prone and not recommended.
How do I add a new platform to an existing Expo module?
Use create-expo-module add-platform-support instead of manually copying native directories. This ensures correct file placement and configuration.
What platforms does this skill cover?
Swift (iOS), Kotlin (Android), and TypeScript. The skill provides DSL patterns for all three to create modules, native views, and shared objects.
Do I need to manually edit expo-module.config.json?
The scaffold generates it automatically, but you may need to edit it to register native modules and configure platform-specific settings like fully-qualified class names for Android.

Generated from the current SKILL.md. These answers refresh after source changes.