Skip to content

Commit c70553f

Browse files
committed
Add use_script_element_for_initial_page config option
1 parent 49947f3 commit c70553f

File tree

9 files changed

+262
-6
lines changed

9 files changed

+262
-6
lines changed

app/views/inertia.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div id="app" data-page="<%= page.to_json %>"></div>
1+
<%= inertia_root(page: page) %>

docs/guide/client-side-setup.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,6 @@ createInertiaApp({
292292
// ...
293293
})
294294
```
295+
296+
> [!NOTE]
297+
> Make sure the [`root_dom_id`](/guide/configuration#root_dom_id) configuration option matches the `id` property in your client-side setup.

docs/guide/configuration.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,59 @@ The default value will be changed to `true` in the next major version.
151151
Specifies the base controller class for the internal `StaticController` used to render [Shorthand routes](/guide/routing#shorthand-routes).
152152

153153
By default, Inertia Rails creates a `StaticController` that inherits from `ApplicationController`. You can use this option to specify a different base controller (for example, to include custom authentication, layout, or before actions).
154+
155+
### `root_dom_id`
156+
157+
**Default**: `'app'`
158+
**ENV**: `INERTIA_ROOT_DOM_ID`
159+
160+
@available_since rails=master
161+
162+
Specifies the DOM element ID used for the root Inertia.js element.
163+
164+
```ruby
165+
InertiaRails.configure do |config|
166+
config.root_dom_id = 'inertia-app'
167+
end
168+
```
169+
170+
> [!NOTE]
171+
> Make sure your client-side Inertia setup uses the same ID when calling `createInertiaApp`.
172+
173+
### `use_script_element_for_initial_page`
174+
175+
**Default**: `false`
176+
**ENV**: `INERTIA_USE_SCRIPT_ELEMENT_FOR_INITIAL_PAGE`
177+
178+
@available_since rails=master core=master
179+
180+
When enabled the initial page data is rendered in a `<script type="application/json">` element instead of the `data-page` attribute on the root `<div>`.
181+
This provides two main benefits:
182+
183+
1. **Smaller page size**: JSON data doesn't require HTML entity encoding, reducing the overall HTML payload size.
184+
2. **Faster parsing**: The browser can parse raw JSON directly from the script element, which is more efficient than parsing HTML-encoded JSON from an attribute.
185+
186+
```ruby
187+
InertiaRails.configure do |config|
188+
config.use_script_element_for_initial_page = true
189+
end
190+
```
191+
192+
When disabled (default), the HTML output looks like:
193+
194+
```html
195+
<div id="app" data-page='{"component":"Users/Index",...}'></div>
196+
```
197+
198+
When enabled, the HTML output looks like:
199+
200+
```html
201+
<script data-page="app" type="application/json">
202+
{"component":"Users/Index",...}
203+
</script>
204+
<div id="app"></div>
205+
```
206+
207+
> [!NOTE]
208+
> When using this option make sure your client-side Inertia setup is configured to read the page data from the `<script>` element.
209+
> See the [protocol documentation](/guide/the-protocol#html-responses) for more details.

docs/guide/responses.md

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,85 @@ Please note that if you manually provide a props hash in your render call, the i
8787
8888
## Root template data
8989

90-
There are situations where you may want to access your prop data in your ERB template. For example, you may want to add a meta description tag, Twitter card meta tags, or Facebook Open Graph meta tags. You can access this data via the `page` method.
90+
There are situations where you may want to access your prop data in your ERB template. For example, you may want to add a meta description tag, Twitter card meta tags, or Facebook Open Graph meta tags.
91+
92+
Inertia Rails provides several view helpers for working with Inertia data in your templates:
93+
94+
### `inertia_page`
95+
96+
Returns the [page object](/guide/the-protocol#the-page-object) hash containing `component`, `props`, `url`, and other Inertia data. Use this to access page data in your root template.
9197

9298
```erb
9399
# app/views/inertia.html.erb
94100
95101
<% content_for(:head) do %>
96-
<meta name="twitter:title" content="<%= page["props"]["event"].title %>">
102+
<meta name="twitter:title" content="<%= inertia_page[:props][:event][:title] %>">
97103
<% end %>
98104
99-
<div id="app" data-page="<%= page.to_json %>"></div>
105+
<%= inertia_root %>
100106
```
101107

102-
Sometimes you may even want to provide data to the root template that will not be sent to your JavaScript page / component. This can be accomplished by passing the `view_data` option.
108+
### `inertia_rendering?`
109+
110+
Returns `true` when the current request is rendering an Inertia response. This is useful for conditionally rendering content in shared layouts.
111+
112+
```erb
113+
# app/views/layouts/application.html.erb
114+
115+
<% if inertia_rendering? %>
116+
<%= yield %>
117+
<% else %>
118+
<div class="traditional-layout">
119+
<%= yield %>
120+
</div>
121+
<% end %>
122+
```
123+
124+
### `inertia_root`
125+
126+
Renders the root element for the Inertia app. This helper automatically respects the [`root_dom_id`](/guide/configuration#root_dom_id) and [`use_script_element_for_initial_page`](/guide/configuration#use_script_element_for_initial_page) configuration options.
127+
128+
```erb
129+
<%= inertia_root %>
130+
```
131+
132+
You can also pass a custom `id` or `page` object if needed:
133+
134+
```erb
135+
<%= inertia_root(id: 'my-app', page: custom_page_object) %>
136+
```
137+
138+
### `inertia_ssr_head`
139+
140+
Returns the SSR head content when [server-side rendering](/guide/server-side-rendering) is enabled. This should be included in your layout's `<head>` section to inject SSR-generated meta tags and other head elements.
141+
142+
```erb
143+
# app/views/layouts/inertia.html.erb
144+
145+
<!DOCTYPE html>
146+
<html>
147+
<head>
148+
<%= inertia_ssr_head %>
149+
</head>
150+
<body>
151+
<%= yield %>
152+
</body>
153+
</html>
154+
```
155+
156+
### `inertia_meta_tags`
157+
158+
Renders meta tags that were defined server-side using the `inertia_meta` configuration. This is useful for SEO when you want to manage meta tags from your Rails controllers. See the [Server-managed meta tags cookbook](/cookbook/server-managed-meta-tags) for a complete guide.
159+
160+
```erb
161+
<head>
162+
<%= inertia_meta_tags %>
163+
</head>
164+
```
165+
166+
### Passing additional data to the view
167+
168+
Sometimes you may want to provide data to the root template that will not be sent to your JavaScript page / component. This can be accomplished by passing the `view_data` option.
103169

104170
```ruby
105171
def show
@@ -120,7 +186,7 @@ You can then access this variable like a regular local variable.
120186
content="<%= local_assigns.fetch(:meta, "Default description") %>">
121187
<% end %>
122188
123-
<div id="app" data-page="<%= page.to_json %>"></div>
189+
<%= inertia_root %>
124190
```
125191

126192
## Rails generators

docs/guide/the-protocol.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,33 @@ Content-Type: text/html; charset=utf-8
2929
</html>
3030
```
3131

32+
### Script element alternative
33+
34+
As an alternative the page data can be rendered in a `<script type="application/json">` element instead of the `data-page` attribute.
35+
This approach provides smaller page sizes (no HTML entity encoding required) and faster JSON parsing by the browser.
36+
37+
```html
38+
<script data-page="app" type="application/json">
39+
{
40+
"component": "Event",
41+
"props": {
42+
"errors": {},
43+
"event": {
44+
"id": 80,
45+
"title": "Birthday party",
46+
"start_date": "2019-06-02",
47+
"description": "Come out and celebrate Jonathan's 36th birthday party!"
48+
}
49+
},
50+
"url": "/events/80",
51+
"version": "c32b8e4965f418ad16eaebba1d4e960f"
52+
}
53+
</script>
54+
<div id="app"></div>
55+
```
56+
57+
See the [`use_script_element_for_initial_page`](/guide/configuration#use_script_element_for_initial_page) configuration option to enable this behavior.
58+
3259
While the initial response is HTML, Inertia does not server-side render the JavaScript page components. For information on server-side rendering, see the [SSR documentation](/guide/server-side-rendering).
3360

3461
## Inertia responses

lib/generators/inertia/install/templates/initializer.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
config.version = ViteRuby.digest
55
config.encrypt_history = true
66
config.always_include_errors_hash = true
7+
config.use_script_element_for_initial_page = true
78
end

lib/inertia_rails/configuration.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ class Configuration
3434

3535
# Whether to include empty `errors` hash to the props when no errors are present.
3636
always_include_errors_hash: nil,
37+
38+
# Whether to use `<script>` element for initial page rendering instead of the `data-page` attribute.
39+
use_script_element_for_initial_page: false,
40+
41+
# DOM id to use for the root Inertia.js element.
42+
root_dom_id: 'app',
3743
}.freeze
3844

3945
OPTION_NAMES = DEFAULTS.keys.freeze

lib/inertia_rails/helper.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,19 @@ def inertia_meta_tags
3232

3333
safe_join(meta_tags, "\n")
3434
end
35+
36+
def inertia_root(id: nil, page: inertia_page)
37+
config = controller.send(:inertia_configuration)
38+
id ||= config.root_dom_id
39+
40+
if config.use_script_element_for_initial_page
41+
safe_join([
42+
tag.script(page.to_json.html_safe, 'data-page': id, type: 'application/json'),
43+
tag.div(id: id)
44+
], "\n")
45+
else
46+
tag.div(id: id, 'data-page': page.to_json)
47+
end
48+
end
3549
end
3650
end

spec/inertia/rendering_spec.rb

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,89 @@
797797
end
798798
end
799799
end
800+
801+
context 'view configuration options' do
802+
after do
803+
InertiaRails.configure do |config|
804+
config.use_script_element_for_initial_page = false
805+
config.root_dom_id = 'app'
806+
end
807+
end
808+
809+
describe 'root_dom_id' do
810+
let(:page) { InertiaRails::Renderer.new('TestComponent', controller, request, response, '').send(:page) }
811+
812+
context 'with default root_dom_id' do
813+
before { get component_path }
814+
815+
it 'renders div with id="app"' do
816+
expect(response.body).to include('<div id="app" data-page=')
817+
end
818+
end
819+
820+
context 'with custom root_dom_id' do
821+
before do
822+
InertiaRails.configure { |c| c.root_dom_id = 'custom-root' }
823+
get component_path
824+
end
825+
826+
it 'renders div with custom id' do
827+
expect(response.body).to include('<div id="custom-root" data-page=')
828+
end
829+
end
830+
end
831+
832+
describe 'use_script_element_for_initial_page' do
833+
let(:page) { InertiaRails::Renderer.new('TestComponent', controller, request, response, '').send(:page) }
834+
835+
context 'when false (default)' do
836+
before { get component_path }
837+
838+
it 'renders div with data-page attribute' do
839+
expect(response.body).to include('<div id="app" data-page=')
840+
end
841+
842+
it 'does not render script element for page data' do
843+
expect(response.body).not_to include('<script data-page=')
844+
end
845+
end
846+
847+
context 'when true' do
848+
before do
849+
InertiaRails.configure { |c| c.use_script_element_for_initial_page = true }
850+
get component_path
851+
end
852+
853+
it 'renders script element with page data' do
854+
expect(response.body).to include('<script data-page="app" type="application/json">')
855+
expect(response.body).to include(page.to_json)
856+
expect(response.body).to include('</script>')
857+
end
858+
859+
it 'renders div without data-page attribute' do
860+
expect(response.body).to include('<div id="app"></div>')
861+
end
862+
end
863+
864+
context 'when true with custom root_dom_id' do
865+
before do
866+
InertiaRails.configure do |c|
867+
c.use_script_element_for_initial_page = true
868+
c.root_dom_id = 'inertia-app'
869+
end
870+
get component_path
871+
end
872+
873+
it 'renders script element with custom data-page attribute' do
874+
expect(response.body).to include('<script data-page="inertia-app" type="application/json">')
875+
end
876+
877+
it 'renders div with custom id' do
878+
expect(response.body).to include('<div id="inertia-app"></div>')
879+
end
880+
end
881+
end
882+
end
800883
end
801884

802885
def inertia_div(page)

0 commit comments

Comments
 (0)