```
### Merge Options
```ruby
# Append to array (default)
InertiaRails.merge { items }
# Prepend to array
InertiaRails.merge(prepend: true) { items }
# Target specific key
InertiaRails.merge(append: 'data') { { data: items, meta: meta } }
# Update matching items instead of duplicating
InertiaRails.merge(match_on: 'id') { items }
```
## Polling
Real-time updates without WebSockets:
```vue
```
### Throttling in Background
```javascript
// Default: 90% throttle in background tabs
usePoll(5000)
// Keep polling at full speed in background
usePoll(5000, {}, { keepAlive: true })
```
## Progress Indicators
### Default NProgress
```javascript
createInertiaApp({
progress: {
delay: 250, // Show after 250ms (skip quick loads)
color: '#29d', // Progress bar color
includeCSS: true, // Include default styles
showProgress: true // Show percentage
},
})
```
### Disable for Specific Requests
```javascript
router.visit('/quick-action', {
showProgress: false
})
```
### Async Requests
```javascript
// Background request without progress indicator
router.post('/analytics/track', { event: 'view' }, {
async: true,
showProgress: false
})
// Async with progress
router.post('/upload', formData, {
async: true,
showProgress: true
})
```
## Once Props
Data resolved once and remembered across navigations:
```ruby
inertia_share do
{
# Evaluated once per session, not on every navigation
app_config: InertiaRails.once { AppConfig.to_json },
feature_flags: InertiaRails.once { FeatureFlags.current }
}
end
```
Combined with optional/deferred:
```ruby
render inertia: {
# Optional + once: resolved only when requested, then remembered
user_preferences: InertiaRails.optional(once: true) {
current_user.preferences.as_json
}
}
```
## Asset Versioning
Ensure users get fresh assets after deployment:
```ruby
# config/initializers/inertia_rails.rb
InertiaRails.configure do |config|
# Using ViteRuby digest
config.version = -> { ViteRuby.digest }
# Or custom version
config.version = -> { ENV['ASSET_VERSION'] || Rails.application.config.assets_version }
end
```
When version changes, Inertia triggers a full page reload instead of XHR.
## Database Query Optimization
### Eager Loading
```ruby
def index
# Bad - N+1 queries
users = User.all
render inertia: {
users: users.map { |u| u.as_json(include: :posts) }
}
# Good - eager load
users = User.includes(:posts)
render inertia: {
users: users.as_json(include: { posts: { only: [:id, :title] } })
}
end
```
### Selective Loading
```ruby
def index
# Only select needed columns
users = User
.select(:id, :name, :email, :created_at)
.includes(:profile)
.order(created_at: :desc)
.limit(50)
render inertia: {
users: users.as_json(
only: [:id, :name, :email],
include: { profile: { only: [:avatar_url] } }
)
}
end
```
## Caching Strategies
### Fragment Caching
```ruby
def index
render inertia: {
stats: Rails.cache.fetch('dashboard_stats', expires_in: 5.minutes) do
compute_expensive_stats
end
}
end
```
### Response Caching with ETags
```ruby
def show
user = User.find(params[:id])
if stale?(user)
render inertia: { user: user.as_json(only: [:id, :name]) }
end
end
```
## Performance Monitoring
### Track Slow Requests
```ruby
# app/controllers/application_controller.rb
around_action :track_request_time
private
def track_request_time
start = Time.current
yield
duration = Time.current - start
if duration > 1.second
Rails.logger.warn "Slow request: #{request.path} took #{duration.round(2)}s"
end
end
```
### Client-Side Metrics
```javascript
router.on('start', (event) => {
event.detail.visit.startTime = performance.now()
})
router.on('finish', (event) => {
const duration = performance.now() - event.detail.visit.startTime
if (duration > 1000) {
console.warn(`Slow navigation to ${event.detail.visit.url}: ${duration}ms`)
}
})
```
## WhenVisible - Lazy Load on Viewport Entry
Load data only when elements become visible using Intersection Observer:
### Basic Usage
```vue
```
### Multiple Props
```vue
```
### Configuration Options
```vue
```
### With Form Submissions
Prevent reloading WhenVisible props after form submission:
```javascript
form.post('/comments', {
except: ['teams'], // Don't reload teams managed by WhenVisible
})
```
## Scroll Management
### Scroll Preservation
```javascript
// Always preserve scroll position
router.visit('/users', { preserveScroll: true })
// Preserve only on validation errors
router.visit('/users', { preserveScroll: 'errors' })
// Conditional preservation
router.visit('/users', {
preserveScroll: (page) => page.props.shouldPreserve
})
```
### Link with Scroll Control
```vue
Users
```
### Scroll Regions
For scrollable containers (not document body):
```vue
```
Inertia tracks and restores scroll position for elements with `scroll-region` attribute.
### Reset Scroll Programmatically
```javascript
router.visit('/users', {
preserveScroll: false, // Reset to top (default)
})
```
## Best Practices Summary
1. **Props**: Return only necessary data, use lazy evaluation
2. **Deferred Props**: Move non-critical data to deferred loading
3. **Partial Reloads**: Refresh only changed data
4. **Code Splitting**: Lazy load pages for large applications
5. **Prefetching**: Preload likely next pages
6. **Infinite Scroll**: Use merge props for seamless pagination
7. **Polling**: Use sparingly with proper throttling
8. **Database**: Eager load associations, select only needed columns
9. **Caching**: Cache expensive computations
10. **Monitoring**: Track and optimize slow requests
11. **WhenVisible**: Lazy load below-the-fold content
12. **Scroll Regions**: Use for complex layouts with multiple scroll areas