inertia-native
Inertia Native
Drive Inertia.js navigation and bridge components from Hotwire Native (iOS & Android).
Hotwire Native injects a turbo.js script into every web view that expects a
window.Turbo object to drive. This package provides a small shim that maps the
native adapter protocol onto Inertia's router, so Inertia pages push, pop, and
restore as native screens — and a web bridge so Inertia pages can use native
bridge components (submit buttons, menus, etc.).
In a regular browser it stays inert: with no native adapter connected, Inertia navigates exactly as usual.
- Framework-agnostic core (
.) — peer-depends on@inertiajs/core. - React bindings (
./react) — optional, peer-depends onreact. - Vue bindings (
./vue) — optional, peer-depends onvue. - Svelte bindings (
./svelte) — optional, peer-depends onsvelte.
Docs: inertia-native.dev. Formerly published as
inertia-hotwire-native. A community project, not affiliated with the
Inertia.js team.
Install
npm add inertia-native
Usage
Call initInertiaNative() once, before createInertiaApp, in your Inertia
entrypoint:
import { createInertiaApp } from '@inertiajs/react'
import { initInertiaNative } from 'inertia-native'
const isNativeApp = !!window.webkit?.messageHandlers?.turbo
initInertiaNative({ debug: import.meta.env.DEV || isNativeApp })
createInertiaApp({ /* ... */ })
That's all that's needed for native navigation (push/pop/replace/restore, modals, forms, error screens, pull-to-refresh).
Form redirects
By default a form's result stays in the web view the form was in. Pass
proposeFormRedirects: true to propose the page a form lands on to native
instead, as Turbo does — native's path configuration then decides, e.g.
dismissing a modal to show the result in the main stack. Validation errors
redirect back to the form's own URL and are not proposed.
initInertiaNative({ proposeFormRedirects: true })
Bridge components (React)
useBridgeComponent(name) is the generic primitive: it returns whether the
connected native app supports the component and a stable send(event, data?, callback?). Build specific components (form, menu, overflow-menu, …) in
your app on top of it.
import { useBridgeComponent } from 'inertia-native/react'
function NativeMenu({ items }) {
const { supported, send } = useBridgeComponent('menu')
if (!supported) return null
return (
<button
onClick={() =>
send('connect', { items }, (message) => onSelect(message.data.index))
}
>
Open menu
</button>
)
}
Each send returns a message id; native replies invoke the callback. The
hook re-checks support when the native handshake completes after mount.
A component that draws native UI on connect should send it again when the
web view returns from a native screen (native:restore, dispatched on
Android). Put restored in the deps of the effect that sends it:
const { supported, send, restored } = useBridgeComponent('button')
useEffect(() => {
if (!supported) return
const id = send('connect', { title }, onTap)
return () => window.HotwireNative?.web?.removeCallback(id)
}, [supported, title, send, restored])
Bridge components (Vue)
The Vue entry exposes the same useBridgeComponent(name) composable.
supported and restored are Refs, so unwrap them with .value (or use
them in a template); send has the same signature.
<script setup>
import { useBridgeComponent } from 'inertia-native/vue'
const props = defineProps(['items'])
const { supported, send } = useBridgeComponent('menu')
function open() {
send('connect', { items: props.items }, (message) => onSelect(message.data.index))
}
</script>
<template>
<button v-if="supported" @click="open">Open menu</button>
</template>
To send connect again after native:restore, watch restored alongside the
other sources of the watcher that sends it.
Bridge components (Svelte)
The Svelte entry exposes useBridgeComponent(name) too. supported and
restored are readable stores (subscribe with $supported, $restored);
send has the same signature. Call it during component initialization — it
registers an onDestroy cleanup.
<script>
import { useBridgeComponent } from 'inertia-native/svelte'
export let items
const { supported, send } = useBridgeComponent('menu')
const open = () =>
send('connect', { items }, (message) => onSelect(message.data.index))
</script>
{#if $supported}
<button on:click={open}>Open menu</button>
{/if}
To send connect again after native:restore, reference $restored in the
reactive statement that sends it.
Requirements
@inertiajs/core>= 3.0react>= 18 (only for the./reactentry)vue>= 3.0 (only for the./vueentry)svelte>= 4.0 (only for the./svelteentry)
License
MIT
Related Packages
Inertia.js adapter for KickJS — server-driven pages rendered by React, Vue or Sv...
InertiaJS server-side adapter for Arkstack, building the modern monolith with se...
Provides access to native geocoding APIs for NativeScript apps (android.location...
This is the description for the plugin seed. This is what will be published to N...
This is the description for the plugin seed. This is what will be published to N...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 1.0.0 | >=3.0.0 | MIT |
Pre-releases (1)
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.0-beta.2 | >=3.0.0 | MIT |