Always props are props that are always included in Inertia responses, even when the client requests a partial reload with specific props. This is perfect for data that must always be fresh, like flash messages or notifications.

The Problem

During partial reloads, only the requested props are included in the response:

@app.get("/dashboard")
async def dashboard(inertia: InertiaDep):
    return inertia.render("Dashboard", {
        "user": get_user(),
        "flash": get_flash_messages(),
        "stats": get_dashboard_stats(),
    })
// Frontend: Only reload stats
router.reload({ only: ['stats'] })

// Result: Only 'stats' is returned
// Problem: 'flash' messages are missing!

The Solution: Always Props

Wrap props with always() to ensure they're included in every response:

from cross_inertia import always

@app.get("/dashboard")
async def dashboard(inertia: InertiaDep):
    return inertia.render("Dashboard", {
        "user": get_user(),
        "flash": always(get_flash_messages),  # Always included!
        "stats": get_dashboard_stats(),
    })
// Frontend: Only reload stats
router.reload({ only: ['stats'] })

// Result: Both 'stats' AND 'flash' are returned

Basic Usage

With a Callable

from cross_inertia import always

@app.get("/dashboard")
async def dashboard(inertia: InertiaDep):
    return inertia.render("Dashboard", {
        "notifications": always(get_notifications),
        "flash": always(get_flash_messages),
    })

With Arguments

Pass arguments to the callable:

from cross_inertia import always

@app.get("/dashboard")
async def dashboard(inertia: InertiaDep):
    user = get_current_user()

    return inertia.render("Dashboard", {
        # Positional arguments
        "notifications": always(get_notifications, user.id),

        # Keyword arguments
        "alerts": always(get_alerts, user_id=user.id, limit=5),
    })

With Static Values

You can also use always() with static values:

from cross_inertia import always

@app.get("/dashboard")
async def dashboard(inertia: InertiaDep):
    return inertia.render("Dashboard", {
        "version": always("1.0.0"),
        "config": always({"theme": "dark"}),
    })

Common Use Cases

Flash Messages

Flash messages should always be delivered, even during partial reloads:

from cross_inertia import always

def get_flash(request: Request) -> dict | None:
    """Get and clear flash message from session."""
    if "flash" in request.session:
        return request.session.pop("flash")
    return None

@app.get("/users")
async def users_list(request: Request, inertia: InertiaDep):
    return inertia.render("Users/List", {
        "users": get_users(),
        "flash": always(get_flash, request),  # Always delivered
    })

Notifications

User notifications should always be fresh:

from cross_inertia import always

@app.get("/dashboard")
async def dashboard(inertia: InertiaDep):
    user = get_current_user()

    return inertia.render("Dashboard", {
        "user": user,
        "stats": get_dashboard_stats(),
        "notifications": always(get_unread_notifications, user.id),
    })
export default function Dashboard({ user, stats, notifications }) {
  const refreshStats = () => {
    // Refresh stats, but notifications are also updated
    router.reload({ only: ['stats'] })
  }

  return (
    <div>
      <NotificationBell count={notifications.length} />
      <button onClick={refreshStats}>Refresh Stats</button>
      <Stats data={stats} />
    </div>
  )
}

CSRF Tokens

Security tokens should always be current:

from cross_inertia import always

@app.get("/settings")
async def settings(request: Request, inertia: InertiaDep):
    return inertia.render("Settings", {
        "user": get_current_user(),
        "csrf_token": always(lambda: request.state.csrf_token),
    })

Real-Time Data

Data that must always reflect the latest state:

from cross_inertia import always

@app.get("/chat/{room_id}")
async def chat_room(room_id: int, inertia: InertiaDep):
    return inertia.render("Chat/Room", {
        "room": get_room(room_id),
        "messages": get_messages(room_id),
        "online_users": always(get_online_users, room_id),  # Always fresh
    })

Cannot Be Excluded

Always props cannot be excluded using the except header:

from cross_inertia import always

@app.get("/dashboard")
async def dashboard(inertia: InertiaDep):
    return inertia.render("Dashboard", {
        "user": get_user(),
        "flash": always(get_flash),
        "stats": get_stats(),
    })
// Frontend: Exclude flash
router.reload({ except: ['flash'] })

// Result: 'flash' is STILL included (always props cannot be excluded)

Combining with Other Prop Types

Always props work alongside optional and deferred props:

from cross_inertia import always, optional, defer

@app.get("/dashboard")
async def dashboard(inertia: InertiaDep):
    return inertia.render("Dashboard", {
        "user": get_user(),                          # Regular prop
        "flash": always(get_flash),                  # Always included
        "expensive_data": optional(get_expensive),   # Only when requested
        "analytics": defer(get_analytics),           # Loaded after render
    })
Prop TypeInitial LoadPartial Reload (only)Partial Reload (except)
RegularIncludedOnly if requestedExcluded if specified
alwaysIncludedAlways includedCannot be excluded
optionalExcludedOnly if requestedExcluded if specified
deferExcludedOnly if requestedExcluded if specified

Best Practices

  1. Use sparingly: Only use always() for props that truly need to be in every response
  2. Keep it lightweight: Always props run on every request, so keep them fast
  3. Perfect for: Flash messages, notifications, CSRF tokens, online status
  4. Avoid for: Large datasets, expensive queries, static data

API Reference

from cross_inertia import always

# With callable
always(callable)

# With callable and positional arguments
always(callable, arg1, arg2)

# With callable and keyword arguments
always(callable, key1=value1, key2=value2)

# With static value
always(static_value)

Next Steps