--- title: 'Tutorial: Creating a native module' sidebar_title: Create a native module description: A tutorial on creating a native module with Expo modules API. --- import { Terminal } from '~/ui/components/Snippet'; import { BoxLink } from '~/ui/components/BoxLink'; import { BookOpen02Icon, Grid01Icon } from '@expo/styleguide-icons'; In this tutorial, we are going to build a module that stores the user's preferred app theme - either dark, light, or system. We'll use [`UserDefaults`](https://developer.apple.com/documentation/foundation/userdefaults) on iOS and [`SharedPreferences`](https://developer.android.com/reference/android/content/SharedPreferences) on Android. It is possible to implement web support using [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), but we'll leave that as an exercise for the reader. ## 1. Initialize a new module First, we'll create a new module. On this page we will use the name `expo-settings`/`ExpoSettings`. You can name it whatever you like, just adjust the instructions accordingly: > **Tip**: Since you aren't going to actually ship this library, you can hit return for all of the prompts to accept the default values. ## 2. Set up our workspace Now let's clean up the default module a little bit so we have more of a clean slate and delete the view module that we won't use in this guide. Find the following files and replace them with the provided minimal boilerplate: ```swift ios/ExpoSettingsModule.swift import ExpoModulesCore public class ExpoSettingsModule: Module { public func definition() -> ModuleDefinition { Name("ExpoSettings") Function("getTheme") { () -> String in "system" } } } ``` ```kotlin android/src/main/java/expo/modules/settings/ExpoSettingsModule.kt package expo.modules.settings import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition class ExpoSettingsModule : Module() { override fun definition() = ModuleDefinition { Name("ExpoSettings") Function("getTheme") { return@Function "system" } } } ``` ```typescript src/index.ts import ExpoSettingsModule from './ExpoSettingsModule'; export function getTheme(): string { return ExpoSettingsModule.getTheme(); } ``` ```typescript example/App.tsx import * as Settings from 'expo-settings'; import { Text, View } from 'react-native'; export default function App() { return ( Theme: {Settings.getTheme()} ); } ``` ## 3. Run the example project Now let's run the example project to make sure everything is working. We'll need to start the TypeScript compiler to watch for changes and rebuild the module JavaScript, and separately in another terminal window we'll compile and run the example app. We should now see the text "Theme: system" in the center of the screen when we launch the example app. The value `"system"` is the result of synchronously calling the `getTheme()` function in the native module. We'll change this value in the next step. ## 4. Get, set, and persist the theme preference value ### iOS native module To read the value on iOS, we can look for a `UserDefaults` string under the key `"theme"`, and fall back to `"system"` if there isn't any. To set the value, we can use `UserDefaults`'s `set(_:forKey:)` method. We'll make our `setTheme` function accept a value of type `String`. ```swift ios/ExpoSettingsModule.swift import ExpoModulesCore public class ExpoSettingsModule: Module { public func definition() -> ModuleDefinition { Name("ExpoSettings") Function("setTheme") { (theme: String) -> Void in UserDefaults.standard.set(theme, forKey:"theme") } Function("getTheme") { () -> String in UserDefaults.standard.string(forKey: "theme") ?? "system" } } } ``` ### Android native module To read the value, we can look for a `SharedPreferences` string under the key `"theme"`, and fall back to `"system"` if there isn't any. We can get the `SharedPreferences` instance from the `reactContext` (a React Native [ContextWrapper](https://developer.android.com/reference/android/content/ContextWrapper)) using `getSharedPreferences()`. To set the value, we can use `SharedPreferences`'s `edit()` method to get an `Editor` instance, and then use `putString()` to set the value. We'll make our `setTheme` function accept a value of type `String`. ```kotlin android/src/main/java/expo/modules/settings/ExpoSettingsModule.kt package expo.modules.settings import android.content.Context import android.content.SharedPreferences import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition class ExpoSettingsModule : Module() { override fun definition() = ModuleDefinition { Name("ExpoSettings") Function("setTheme") { theme: String -> getPreferences().edit().putString("theme", theme).commit() } Function("getTheme") { return@Function getPreferences().getString("theme", "system") } } private val context get() = requireNotNull(appContext.reactContext) private fun getPreferences(): SharedPreferences { return context.getSharedPreferences(context.packageName + ".settings", Context.MODE_PRIVATE) } } ``` ### TypeScript module Now we can call our native modules from TypeScript. ```typescript src/index.ts import ExpoSettingsModule from './ExpoSettingsModule'; export function getTheme(): string { return ExpoSettingsModule.getTheme(); } export function setTheme(theme: string): void { return ExpoSettingsModule.setTheme(theme); } ``` ### Example app We can now use the `Settings` API in our example app. ```typescript example/App.tsx import * as Settings from 'expo-settings'; import { Button, Text, View } from 'react-native'; export default function App() { const theme = Settings.getTheme(); // Toggle between dark and light theme const nextTheme = theme === 'dark' ? 'light' : 'dark'; return ( Theme: {Settings.getTheme()}