Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hotwire Native (and Hotwire 101) for Laravel Devs

Hotwire Native (and Hotwire 101) for Laravel Devs

An introduction to Hotwire Native (and a brief introduction to Hotwire) for Laravel developers.

Links:
- The Hotwire Starter Kit: https://github.com/hotwired-laravel/hotwire-starter-kit
- The Hotwire Native Android Example for the Hotwire Starter Kit: https://github.com/hotwired-laravel/hotwire-starter-kit-android-example
- Joe Masilotti's Bridge Components library: https://github.com/joemasilotti/bridge-components
- The Hotwire Native Demo Web App: https://github.com/hotwired/hotwire-native-demo
- The Hotwire Native Demo Android App: https://github.com/hotwired/hotwire-native-android/tree/main/demo
- The Hotwire Native Demo iOS App: https://github.com/hotwired/hotwire-native-ios/tree/main/Demo
- Jay Ohms introduction to Strada (now Bridge Components, still applies): https://youtu.be/LKBMXqc43Q8?si=ohA7n7-jJtpJJwxV
- Joe Masilotti's introduction to Turbo Native (still applies): https://youtu.be/hAq05KSra2g?si=sOA3etvFImrpk3sP
- Sam Stephenson's "Turbolinks 5: I can't believe it's not native" talk: https://youtu.be/SWEts0rlezA?si=TU8AfgXQPoS-51mq

Avatar for Tony Messias

Tony Messias

April 21, 2025
Tweet

More Decks by Tony Messias

Other Decks in Programming

Transcript

  1. Agenda ➔ What's new in Turbo 8 ➔ Hotwire Starter

    Kit for Laravel ➔ Intro to Hotwire Native ◆ Getting Started ◆ Navigation ◆ Web Forms ◆ Our First Bridge Component ◆ Native Screens
  2. Stimulus 101 import { Controller } from "@hotwired/stimulus" export default

    class extends Controller { static targets = [ "source" ] copy() { navigator.clipboard.writeText(this.sourceTarget.value) } }
  3. Stimulus 101 <div> PIN: <input type="text" value="3737" readonly> <button type="button">

    Copy to Clipboard </button> </div> <div data-controller="clipboard"> PIN: <input data-clipboard-target="source" type="text" value="3737" readonly> <button type="button" data-action="clipboard#copy"> Copy to Clipboard </button> </div>
  4. Getting Started 1. Create Project 2. Add Dependency 3. Set

    Permissions 4. Replace Activity class MainActivity : HotwireActivity() { override fun onCreate(savedInstanceState : Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState ) setContentView(R.layout.activity_main) findViewById <View>(R.id.main_nav_host) .applyDefaultImeWindowInsets() } override fun navigatorConfigurations() = listOf( NavigatorConfiguration( name = "main", startLocation = "http://10.0.2.2:8000" , navigatorHostId = R.id.main_nav_host ) ) }
  5. Getting Started 1. Create Project 2. Add Dependency 3. Add

    Scene Delegate let rootURL = URL(string: "http://localhost:8000")! class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? private let navigator = Navigator() func scene(_ scene: UIScene, ...) { window?.rootViewController = navigator.rootVi... navigator.route(rootURL) } }
  6. Path Configuration { "settings": {}, "rules": [ { "patterns": [".*"],

    "properties": { "context": "default", "uri": "hotwire://fragment/web", "pull_to_refresh_enabled": true } }, { "patterns": ["/create$", "/edit$", "/delete$"], "properties": { "context": "modal", "uri": "hotwire://fragment/web/modal/sheet", "pull_to_refresh_enabled": false }
  7. Bridge Components ✨ ➔ Component-based framework ➔ All about messaging:

    The Native App doesn't know anything about the markup on the page ➔ Native app should opt-in for native components
  8. 2) import { BridgeComponent, BridgeElement } from "@hotwired/hotwire-native-bridge" export default

    class extends BridgeComponent { static component = "form" static targets = ["submit"] connect() { super.connect() const element = new BridgeElement(this.submitTarget) const title = element.bridgeAttribute("title") this.send("connect", { title }, () => { this.submitTarget.click() }) } } Bridge Components
  9. 3) <form action="/profile" > <!-- ... –-> <button type="submit" >Save

    changes</button> </form> Bridge Components <form action="/profile" data-controller="bridge–-form" > <!-- ... –-> <button data-bridge--form-target="submit" data-bridge-title="Save" type="submit" >Save changes</button> </form>
  10. 5) class FormComponent( name: String, private val delegate: BridgeDelegate<HotwireDestination> )

    : BridgeComponent<HotwireDestination>(name, delegate) { override fun onReceive(message: Message) { // Handle incoming messages based on the message `event`. when (message.event) { "connect" -> handleConnectEvent(message) else -> Log.w("FormComponent", "Unknown event for: $message") } } // ... } Bridge Components
  11. 5) class FormComponent( name: String, private val delegate: BridgeDelegate<HotwireDestination> )

    : BridgeComponent<HotwireDestination>(name, delegate) { private fun handleConnectEvent(message: Message) { val data = message.data<MessageData>() ?: return // Write native code to add create button and add to toolbar… btn.formSubmit.apply { text = data.title setOnClickListener { replyTo("connect") } } } } Bridge Components
  12. { "settings": {}, "rules": [ { "patterns": ["/numbers$"], "properties": {

    "uri": "hotwire://fragment/numbers", "title": "Numbers" } } ] } @HotwireDestinationDeepLink (uri = "hotwire://fragment/numbers" ) class NumbersFragment : HotwireFragment() { // ... } 1) 2) Bridge Components 3) Hotwire.registerFragmentDestinations( WebFragment:: class, NumbersFragment:: class, )
  13. Demo ➔ Navigation ➔ Web Forms ➔ Lifting Form Submits

    ➔ Native Toast Messages ➔ Native Screens
  14. Advantages ➔ Small teams ➔ Very little native code needed

    (at least initially) ➔ Low maintenance ➔ Web controls how/when native components or screens are accessed
  15. Disadvantages ➔ No offline story (you gotta go native for

    offline support) ➔ We must know a little bit about native development to build the native app (Kotlin on Android and Swift on iOS)
  16. Links ➔ The Hotwire Starter Kit: https://github.com/hotwired-laravel/hotwire-starter-kit ➔ The Hotwire

    Native Android Example for the Hotwire Starter Kit: https://github.com/hotwired-laravel/hotwire-starter-kit-android-example ➔ Joe Masilotti's Bridge Components library: https://github.com/joemasilotti/bridge-components ➔ The Hotwire Native Demo Web App: https://github.com/hotwired/hotwire-native-demo ➔ The Hotwire Native Demo Android App: https://github.com/hotwired/hotwire-native-android/tree/main/demo ➔ The Hotwire Native Demo iOS App: https://github.com/hotwired/hotwire-native-ios/tree/main/Demo ➔ Jay Ohms introduction to Strada (now Bridge Components, still applies): https://youtu.be/LKBMXqc43Q8?si=ohA7n7-jJtpJJwxV ➔ Joe Masilotti's introduction to Turbo Native (still applies): https://youtu.be/hAq05KSra2g?si=sOA3etvFImrpk3sP ➔ Sam Stephenson's "Turbolinks 5: I can't believe it's not native" talk: https://youtu.be/SWEts0rlezA?si=TU8AfgXQPoS-51mq