A Rails engine that provides native detection, configuration, push device registration, and view helpers for Ruby Native iOS and Android apps.
gem "ruby_native"The engine auto-mounts at /native. No route configuration needed.
Run the install generator to create your config file:
rails generate ruby_native:installThis creates config/ruby_native.yml with sensible defaults. If you have a .claude/ directory, it also adds .claude/ruby_native.md with AI-assisted setup instructions. Follow the printed instructions to:
- Edit the config with your app name, colors, and tabs
- Add
<%= stylesheet_link_tag :ruby_native %>to your layout<head> - Add
<%= native_tabs_tag %>to your layout<body> - Run
bundle exec ruby_native previewto see it on your phone
Using Claude Code? Open it in your project and ask "what do I need to do next?" for guided setup.
Edit config/ruby_native.yml:
appearance:
tint_color: "#4F46E5"
background_color: "#FFFFFF"
status_bar_color: "#FFFFFF"
tabs:
- title: Home
path: /
icon: house
- title: Profile
path: /profile
icon: personColor fields accept a plain hex string or an object with light and dark keys for dark mode:
background_color:
light: "#FFFFFF"
dark: "#1C1C1E"Preview your app on a real device without deploying. This starts a Cloudflare tunnel and displays a QR code for the companion app to scan.
bundle exec ruby_native previewOptions:
--port 3001- specify the local server port (defaults to 3000)
Requires cloudflared. Install with:
brew install cloudflare/cloudflare/cloudflaredThe companion app persists the scanned URL across launches. Long-press the app icon and tap "Switch website" to scan a new server.
GET /native/config- returns the YAML config as JSONPOST /native/push/devices- registers a push notification token (requirescurrent_userfrom host app)
Normal Mode works with any frontend framework and requires no JavaScript. You get tabs, form page marking, push notifications, and history management.
Advanced Mode adds native navigation bar buttons, submit buttons, action menus, and search bars. It requires Stimulus and a small JavaScript setup step (see Advanced Mode setup below). Migration is additive. Start with Normal and add Advanced helpers one page at a time.
Place helpers in the <body>, not the <head>.
native_app?- true when the request comes from a Ruby Native app (checks user agent)native_version- returns the app version as aRubyNative::NativeVersion. Defaults to"0"when the version is unknown. Supports string comparisons:native_version >= "1.4".native_tabs_tag(enabled: true)- shows the native tab bar.native_push_tag- requests push notification permission.native_back_button_tag(text = nil, **options)- renders a back button for Normal Mode. Hidden by default, shown when the native app setsbody.can-go-back. Not needed in Advanced Mode where the system provides a native back button.
native_form_tag- marks the page as a form. The app skips form pages when navigating back.
These require the JavaScript setup described in Advanced Mode setup.
native_form_data- returns the data hash for the native form submit button. Pass toform_with'sdata:option.native_submit_data- returns the data hash for the native submit target. Pass toform.submit'sdata:option.native_button_tag(title, url, ios_image:, side: :right, **options)- adds a native navigation bar button.native_menu_tag(title:, side: :right, &block)- displays a native action sheet menu.native_search_tag- adds a native search bar.
- Install the JavaScript dependency:
yarn add @hotwired/hotwire-native-bridge
# or
bin/importmap pin @hotwired/hotwire-native-bridge- Import the controllers in your JavaScript entrypoint:
import "ruby_native/bridge"<%= form_with model: @link, data: native_form_data do |f| %>
<%= f.text_field :url %>
<%= f.submit "Save", data: native_submit_data %>
<% end %><%= native_button_tag "Add a link", new_link_path, ios_image: "plus", class: "btn btn-primary" %>Options:
ios_image:- SF Symbol icon name (falls back to the title text)side:-:leftor:right(default). Left supplements the back button.
<%= native_menu_tag(title: "Actions") do |menu| %>
<%= menu.item "Edit", edit_link_path(@link) %>
<%= menu.item "Delete", link_path(@link), method: :delete, destructive: true %>
<% end %>Options on native_menu_tag:
title:- action sheet titleside:-:leftor:right(default)
Options on menu.item:
method:- Turbo method (e.g.,:delete)destructive: true- red styling
The gem includes ruby_native.css which controls back button visibility:
<%= stylesheet_link_tag :ruby_native %>This shows .native-back-button elements only when body.can-go-back is set by the native app.