Inertia Helpers¶
- litestar_vite.inertia.helpers.lazy(key: str, value_or_callable: None) StaticProp[str, None][source]¶
- litestar_vite.inertia.helpers.lazy(key: str, value_or_callable: T) StaticProp[str, T]
- litestar_vite.inertia.helpers.lazy(key: str, value_or_callable: Callable[[...], None] = None) DeferredProp[str, None]
- litestar_vite.inertia.helpers.lazy(key: str, value_or_callable: Callable[[...], Coroutine[Any, Any, None]] = None) DeferredProp[str, None]
- litestar_vite.inertia.helpers.lazy(key: str, value_or_callable: Callable[[...], T | Coroutine[Any, Any, T]] = None) DeferredProp[str, T]
Create a lazy prop only included during partial reloads.
Lazy props are excluded from the initial page load and only sent when explicitly requested via partial reload (X-Inertia-Partial-Data header). This optimizes initial page load by deferring non-critical data.
There are two use cases for lazy():
- 1. Static Value (bandwidth optimization):
The value is computed eagerly but only sent during partial reloads. Use when the value is cheap to compute but you want to reduce initial payload.
>>> lazy("user_count", len(users))- 2. Callable (bandwidth + CPU optimization):
The callable is only invoked during partial reloads. Use when the value is expensive to compute.
>>> lazy("permissions", lambda: Permission.all())
Warning
False Lazy Pitfall
Be careful not to accidentally call the function when passing it.
Wrong:
lazy("data", expensive_fn())Correct:
lazy("data", expensive_fn)This is a Python evaluation order issue, not a framework limitation.
- Parameters:
- Returns:
StaticProp if value_or_callable is not callable, DeferredProp otherwise.
Example:
from litestar_vite.inertia import lazy, InertiaResponse @get("/dashboard", component="Dashboard") async def dashboard() -> InertiaResponse: props = { "user": current_user, "user_count": lazy("user_count", 42), "permissions": lazy("permissions", lambda: Permission.all()), "notifications": lazy("notifications", fetch_notifications), } return InertiaResponse(props)
See also
defer(): For v2 grouped deferred props loaded after page renderInertia.js partial reloads: https://inertiajs.com/partial-reloads
- litestar_vite.inertia.helpers.defer(key: str, callback: Callable[[...], T | Coroutine[Any, Any, T]], group: str = 'default') DeferredProp[str, T][source]¶
Create a deferred prop with optional grouping (v2 feature).
Deferred props are loaded lazily after the initial page render. Props in the same group are fetched together in a single request.
- Parameters:
- Returns:
A DeferredProp instance.
Example:
defer("permissions", lambda: Permission.all()) defer("teams", lambda: Team.all(), group="attributes") defer("projects", lambda: Project.all(), group="attributes") # Chain with .once() for lazy + cached behavior defer("stats", lambda: compute_expensive_stats()).once()
- litestar_vite.inertia.helpers.once(key: str, value_or_callable: T | Callable[[...], T | Coroutine[Any, Any, T]]) OnceProp[str, T][source]¶
Create a prop that resolves once and is cached client-side (v2.2.20+ feature).
Once props are included in the initial page load and resolved immediately. After resolution, the client caches the value and won’t request it again on subsequent page visits, unless explicitly requested via partial reload.
This is useful for: - Expensive computations that rarely change - User preferences or settings - Feature flags - Static configuration
Unlike lazy props, once props ARE included in initial loads. The “once” behavior tells the client to cache the result.
- Parameters:
- Returns:
An OnceProp instance.
Example:
from litestar_vite.inertia import once, InertiaResponse @get("/dashboard", component="Dashboard") async def dashboard() -> InertiaResponse: return InertiaResponse({ "user": current_user, "settings": once("settings", lambda: Settings.for_user(user_id)), "feature_flags": once("feature_flags", get_feature_flags()), })
See also
defer(): For deferred props that support.once()chainingInertia.js once props: https://inertiajs.com/partial-reloads#once
- litestar_vite.inertia.helpers.optional(key: str, callback: Callable[[...], T | Coroutine[Any, Any, T]]) OptionalProp[str, T][source]¶
Create a prop only included when explicitly requested (v2 feature).
Optional props are NEVER included in initial page loads or standard partial reloads. They’re only sent when the client explicitly requests them via
only: ['prop_name']in a partial reload.This is designed for use with Inertia’s WhenVisible component, which triggers a partial reload requesting specific props when an element becomes visible in the viewport.
The callback is only evaluated when requested, providing both bandwidth and CPU optimization.
- Parameters:
- Returns:
An OptionalProp instance.
Example:
from litestar_vite.inertia import optional, InertiaResponse @get("/posts/{post_id}", component="Posts/Show") async def show_post(post_id: int) -> InertiaResponse: post = await Post.get(post_id) return InertiaResponse({ "post": post, # Only loaded when WhenVisible triggers "comments": optional("comments", lambda: Comment.for_post(post_id)), "related_posts": optional("related_posts", lambda: Post.related(post_id)), })
Frontend usage with WhenVisible:
<WhenVisible data="comments" :params="{ only: ['comments'] }"> <template #fallback> <LoadingSpinner /> </template> <CommentList :comments="comments" /> </WhenVisible>
See also
Inertia.js WhenVisible: https://inertiajs.com/load-when-visible
- litestar_vite.inertia.helpers.always(key: str, value: T) AlwaysProp[str, T][source]¶
Create a prop always included, even during partial reloads (v2 feature).
Always props bypass partial reload filtering entirely. They’re included in every response regardless of what keys the client requests.
Use for critical data that must always be present: - Authentication state - Permission flags - Feature toggles - Error states
- Parameters:
- Returns:
An AlwaysProp instance.
Example:
from litestar_vite.inertia import always, lazy, InertiaResponse @get("/dashboard", component="Dashboard") async def dashboard(request: Request) -> InertiaResponse: return InertiaResponse({ # Always sent, even during partial reloads for other props "auth": always("auth", {"user": request.user, "can": permissions}), # Only sent when explicitly requested "analytics": lazy("analytics", get_analytics), "reports": lazy("reports", get_reports), })
See also
lazy(): For props excluded from initial loadoptional(): For props only included when explicitly requested
- class litestar_vite.inertia.helpers.PropFilter[source]¶
Bases:
objectConfiguration for prop filtering during partial reloads.
Used with
only()andexcept_()helpers to explicitly control which props are sent during partial reload requests.
- litestar_vite.inertia.helpers.only(*keys: str) PropFilter[source]¶
Create a filter that only includes the specified prop keys.
Use this to explicitly limit which props are sent during partial reloads. Only the specified props will be included in the response.
- Parameters:
*keys¶ – The prop keys to include.
- Returns:
A PropFilter configured to include only the specified keys.
Example:
from litestar_vite.inertia import only, InertiaResponse @get("/users", component="Users") async def list_users( request: InertiaRequest, user_service: UserService, ) -> InertiaResponse: return InertiaResponse( { "users": user_service.list(), "teams": team_service.list(), "stats": stats_service.get(), }, prop_filter=only("users"), )
Note
This is a server-side helper. The client should use Inertia’s
router.reload({ only: ['users'] })for client-initiated filtering.
- litestar_vite.inertia.helpers.except_(*keys: str) PropFilter[source]¶
Create a filter that excludes the specified prop keys.
Use this to explicitly exclude certain props during partial reloads. All props except the specified ones will be included in the response.
- Parameters:
*keys¶ – The prop keys to exclude.
- Returns:
A PropFilter configured to exclude the specified keys.
Example:
from litestar_vite.inertia import except_, InertiaResponse @get("/users", component="Users") async def list_users( request: InertiaRequest, user_service: UserService, ) -> InertiaResponse: return InertiaResponse( { "users": user_service.list(), "teams": team_service.list(), "stats": expensive_stats(), }, prop_filter=except_("stats"), )
Note
The function is named
except_with a trailing underscore to avoid conflicting with Python’sexceptkeyword.
- class litestar_vite.inertia.helpers.MergeProp[source]¶
Bases:
Generic[PropKeyT,T]A wrapper for merge prop configuration (v2 feature).
Merge props allow data to be combined with existing props during partial reloads instead of replacing them entirely.
- litestar_vite.inertia.helpers.merge(key: str, value: T, strategy: Literal['append', 'prepend', 'deep'] = 'append', match_on: str | list[str] | None = None) MergeProp[str, T][source]¶
Create a merge prop for combining data during partial reloads (v2 feature).
Merge props allow new data to be combined with existing props rather than replacing them entirely. This is useful for infinite scroll, load more buttons, and similar patterns.
Note: Prop merging only works during partial reloads. Full page visits will always replace props entirely.
- Parameters:
key¶ – The prop key.
value¶ – The value to merge.
strategy¶ – How to merge the data: - ‘append’: Add new items to the end (default) - ‘prepend’: Add new items to the beginning - ‘deep’: Recursively merge nested objects
match_on¶ – Optional key(s) to match items on during merge, useful for updating existing items instead of duplicating.
- Returns:
A MergeProp instance.
Example:
merge("posts", new_posts) merge("messages", new_messages, strategy="prepend") merge("user_data", updates, strategy="deep") merge("posts", updated_posts, match_on="id")
- litestar_vite.inertia.helpers.scroll_props(page_name: str = 'page', current_page: int = 1, previous_page: int | None = None, next_page: int | None = None) ScrollPropsConfig[source]¶
Create scroll props configuration for infinite scroll (v2 feature).
Scroll props allow Inertia to manage pagination state for infinite scroll patterns, providing next/previous page information to the client.
- Parameters:
- Returns:
A ScrollPropsConfig instance for use in InertiaResponse.
Example:
from litestar_vite.inertia import scroll_props, InertiaResponse @get("/posts", component="Posts") async def list_posts(page: int = 1) -> InertiaResponse: posts = await Post.paginate(page=page, per_page=20) return InertiaResponse( {"posts": merge("posts", posts.items)}, scroll_props=scroll_props( current_page=page, previous_page=page - 1 if page > 1 else None, next_page=page + 1 if posts.has_more else None, ), )
- litestar_vite.inertia.helpers.is_merge_prop(value: Any) TypeGuard[MergeProp[Any, Any]][source]¶
Check if value is a MergeProp.
- Parameters:
value¶ – Any value to check
- Returns:
True if value is a MergeProp
- litestar_vite.inertia.helpers.extract_merge_props(props: dict[str, Any]) tuple[list[str], list[str], list[str], dict[str, list[str]]][source]¶
Extract merge props metadata for the Inertia v2 protocol.
This extracts all MergeProp instances from the props dict and categorizes them by their merge strategy, returning the appropriate lists for the page response.
- Parameters:
props¶ – The props dictionary to scan.
- Returns:
A tuple of (merge_props, prepend_props, deep_merge_props, match_props_on) where each list contains the prop keys for that strategy, and match_props_on is a dict mapping prop keys to the keys to match on.
Example:
props = { "users": [...], "posts": merge("posts", new_posts), "messages": merge("messages", new_msgs, strategy="prepend"), "data": merge("data", updates, strategy="deep"), "items": merge("items", items, match_on="id"), } merge_props, prepend_props, deep_merge_props, match_props_on = extract_merge_props(props) The returned values then contain: - merge_props: ["posts", "items"] - prepend_props: ["messages"] - deep_merge_props: ["data"] - match_props_on: {"items": ["id"]}
- class litestar_vite.inertia.helpers.StaticProp[source]¶
Bases:
Generic[PropKeyT,StaticT]A wrapper for static property evaluation.
- class litestar_vite.inertia.helpers.DeferredProp[source]¶
Bases:
AsyncRenderMixin,Generic[PropKeyT,T]A wrapper for deferred property evaluation.
- __init__(key: PropKeyT, value: Callable[[...], T | Coroutine[Any, Any, T] | None] | None = None, group: str = 'default', is_once: bool = False) None[source]¶
- property is_once: bool¶
Whether this prop should only be resolved once and cached client-side.
- Returns:
True if this is a once prop.
- once() DeferredProp[PropKeyT, T][source]¶
Return a new DeferredProp with once behavior enabled.
Once props are cached client-side after first resolution. They won’t be re-fetched on subsequent visits unless explicitly requested via partial reload.
- Returns:
A new DeferredProp with is_once=True.
Example:
# Combine defer with once for lazy + cached behavior defer("stats", lambda: compute_expensive_stats()).once()
- static with_portal(portal: BlockingPortal | None = None) Generator[BlockingPortal, None, None]¶
Get or create a blocking portal for async execution.
- Parameters:
portal¶ – Optional existing portal to reuse. If None, creates a new one.
- Yields:
A BlockingPortal for executing async code from sync context.
Example:
with self.with_portal(portal) as p: result = p.call(async_function)
- class litestar_vite.inertia.helpers.OnceProp[source]¶
Bases:
AsyncRenderMixin,Generic[PropKeyT,T]A wrapper for once-only property evaluation (v2.2.20+ feature).
Once props are resolved once and cached client-side. They won’t be re-fetched on subsequent page visits unless explicitly requested via partial reload with
only: ['key'].This is useful for expensive computations that rarely change (e.g., user preferences, feature flags, static configuration).
Unlike lazy props, once props ARE included in the initial page load. The “once” behavior tells the client to cache the value and not request it again on future visits.
- __init__(key: PropKeyT, value: T | Callable[[...], T | Coroutine[Any, Any, T]]) None[source]¶
Initialize a OnceProp.
- render(portal: BlockingPortal | None = None) T | None[source]¶
Render the prop value, caching the result.
- Parameters:
portal¶ – Optional blocking portal for async callbacks.
- Returns:
The rendered value.
- static with_portal(portal: BlockingPortal | None = None) Generator[BlockingPortal, None, None]¶
Get or create a blocking portal for async execution.
- Parameters:
portal¶ – Optional existing portal to reuse. If None, creates a new one.
- Yields:
A BlockingPortal for executing async code from sync context.
Example:
with self.with_portal(portal) as p: result = p.call(async_function)
- class litestar_vite.inertia.helpers.OptionalProp[source]¶
Bases:
AsyncRenderMixin,Generic[PropKeyT,T]A wrapper for optional property evaluation (v2 feature).
Optional props are NEVER included in initial page loads or standard partial reloads. They’re only sent when the client explicitly requests them via
only: ['prop_name'].This is designed for use with Inertia’s WhenVisible component, which loads data only when an element becomes visible in the viewport.
The callback is only evaluated when the prop is explicitly requested, providing both bandwidth and CPU optimization.
- __init__(key: PropKeyT, callback: Callable[[...], T | Coroutine[Any, Any, T]]) None[source]¶
Initialize an OptionalProp.
- render(portal: BlockingPortal | None = None) T | None[source]¶
Render the prop value, caching the result.
- Parameters:
portal¶ – Optional blocking portal for async callbacks.
- Returns:
The rendered value.
- static with_portal(portal: BlockingPortal | None = None) Generator[BlockingPortal, None, None]¶
Get or create a blocking portal for async execution.
- Parameters:
portal¶ – Optional existing portal to reuse. If None, creates a new one.
- Yields:
A BlockingPortal for executing async code from sync context.
Example:
with self.with_portal(portal) as p: result = p.call(async_function)
- class litestar_vite.inertia.helpers.AlwaysProp[source]¶
Bases:
Generic[PropKeyT,T]A wrapper for always-included property evaluation (v2 feature).
Always props are ALWAYS included in responses, even during partial reloads. This is the opposite of lazy props - they bypass any partial reload filtering.
Use for critical data that must always be present, such as: - Authentication state - Permission flags - Feature toggles - Error states
- litestar_vite.inertia.helpers.is_lazy_prop(value: Any) TypeGuard[DeferredProp[Any, Any] | StaticProp[Any, Any]][source]¶
Check if value is a lazy property (StaticProp or DeferredProp).
Lazy props are excluded from initial page loads and only sent when explicitly requested via partial reload.
- Parameters:
value¶ – Any value to check
- Returns:
True if value is a lazy property (StaticProp or DeferredProp)
- litestar_vite.inertia.helpers.is_once_prop(value: Any) TypeGuard[OnceProp[Any, Any]][source]¶
Check if value is a once prop.
Once props are included in initial loads but cached client-side.
- Parameters:
value¶ – Any value to check
- Returns:
True if value is an OnceProp
- litestar_vite.inertia.helpers.is_optional_prop(value: Any) TypeGuard[OptionalProp[Any, Any]][source]¶
Check if value is an optional prop.
Optional props are only included when explicitly requested.
- Parameters:
value¶ – Any value to check
- Returns:
True if value is an OptionalProp
- litestar_vite.inertia.helpers.is_always_prop(value: Any) TypeGuard[AlwaysProp[Any, Any]][source]¶
Check if value is an always prop.
Always props bypass partial reload filtering.
- Parameters:
value¶ – Any value to check
- Returns:
True if value is an AlwaysProp
- litestar_vite.inertia.helpers.is_special_prop(value: Any) bool[source]¶
Check if value is any special prop type (lazy, once, optional, always).
- Parameters:
value¶ – Any value to check
- Returns:
True if value is a special prop wrapper
- litestar_vite.inertia.helpers.is_deferred_prop(value: Any) TypeGuard[DeferredProp[Any, Any]][source]¶
Check if value is specifically a DeferredProp (not StaticProp).
- Parameters:
value¶ – Any value to check
- Returns:
True if value is a DeferredProp
- litestar_vite.inertia.helpers.extract_deferred_props(props: dict[str, Any]) dict[str, list[str]][source]¶
Extract deferred props metadata for the Inertia v2 protocol.
This extracts all DeferredProp instances from the props dict and groups them by their group name, returning a dict mapping group -> list of prop keys.
Note: DeferredProp instances with is_once=True are excluded from the result because once props should not be re-fetched after initial resolution.
- Parameters:
props¶ – The props dictionary to scan.
- Returns:
A dict mapping group names to lists of prop keys in that group. Empty dict if no deferred props found.
Example:
props = { "users": [...], "teams": defer("teams", get_teams, group="attributes"), "projects": defer("projects", get_projects, group="attributes"), "permissions": defer("permissions", get_permissions), } result = extract_deferred_props(props) The result is {"default": ["permissions"], "attributes": ["teams", "projects"]}.
- litestar_vite.inertia.helpers.extract_once_props(props: dict[str, Any]) list[str][source]¶
Extract once props for the Inertia v2.2.20+ protocol.
Once props are cached client-side after first resolution. This function extracts all OnceProp instances and DeferredProp instances with is_once=True.
- Parameters:
props¶ – The props dictionary to scan.
- Returns:
A list of prop keys that should be cached client-side. Empty list if no once props found.
Example:
props = { "user": current_user, "settings": once("settings", get_settings), "stats": defer("stats", get_stats).once(), } result = extract_once_props(props) The result is ["settings", "stats"].
- litestar_vite.inertia.helpers.should_render(value: Any, partial_data: set[str] | None = None, partial_except: set[str] | None = None, key: str | None = None) bool[source]¶
Check if value should be rendered based on partial reload filtering.
For v2 protocol, partial_except takes precedence over partial_data. When a key is provided, filtering applies to all props (not just lazy props).
Prop types have different behaviors: - AlwaysProp: Always included, bypasses all filtering - OptionalProp: Only included when explicitly requested via partial_data - LazyProp (StaticProp/DeferredProp): Excluded from initial load, included on partial reload - OnceProp: Included in initial load, cached client-side - Regular values: Follow standard partial reload filtering
- Parameters:
- Returns:
True if value should be rendered
- Return type:
- litestar_vite.inertia.helpers.is_or_contains_lazy_prop(value: Any) bool[source]¶
Check if value is or contains a deferred property.
- Parameters:
value¶ – Any value to check
- Returns:
True if value is or contains a deferred property
- litestar_vite.inertia.helpers.is_or_contains_special_prop(value: Any) bool[source]¶
Check if value is or contains any special prop type.
This includes lazy, once, optional, and always props.
- Parameters:
value¶ – Any value to check
- Returns:
True if value is or contains a special prop
- litestar_vite.inertia.helpers.lazy_render(value: T, partial_data: set[str] | None = None, portal: BlockingPortal | None = None, partial_except: set[str] | None = None) T[source]¶
Filter deferred properties from the value based on partial data.
For v2 protocol, partial_except takes precedence over partial_data.
Return shared session props for a request.
For v2 protocol, partial_except takes precedence over partial_data.
- Parameters:
- Returns:
The shared props. Includes a special
_once_propskey (list of prop keys that were OnceProp instances) for protocol metadata generation.
Note
Be sure to call this before self.create_template_context if you would like to include the flash message details.
Share a value in the session.
Shared values are included in the props of every Inertia response for the current request. This is useful for data that should be available to all components (e.g., authenticated user, permissions, settings).
- litestar_vite.inertia.helpers.error(connection: ASGIConnection[Any, Any, Any, Any], key: str, message: str) bool[source]¶
Set an error message in the session.
Error messages are included in the
errorsprop of Inertia responses, typically used for form validation errors. The key usually corresponds to a form field name.
- litestar_vite.inertia.helpers.flash(connection: ASGIConnection[Any, Any, Any, Any], message: str, category: str = 'info') bool[source]¶
Add a flash message to the session.
Flash messages are stored in the session and passed to the frontend via the flash prop in every Inertia response. They’re automatically cleared after being displayed (pop semantics).
This function works without requiring Litestar’s FlashPlugin or any Jinja2 template configuration, making it ideal for SPA-only Inertia applications.
- Parameters:
- Returns:
True if the flash message was successfully stored, False otherwise.
Example:
from litestar_vite.inertia import flash @post("/create") async def create_item(request: Request) -> InertiaResponse: flash(request, "Item created successfully!", "success") return InertiaResponse(...)
- litestar_vite.inertia.helpers.clear_history(connection: ASGIConnection[Any, Any, Any, Any]) None[source]¶
Mark that the next response should clear client history encryption keys.
This function sets a session flag that will be consumed by the next InertiaResponse, causing it to include clearHistory: true in the page object. The Inertia client will then regenerate its encryption key, invalidating all previously encrypted history entries.
This should typically be called during logout to ensure sensitive data cannot be recovered from browser history after a user logs out.
- Parameters:
connection¶ – The ASGI connection (Request).
Note
Requires session middleware to be configured. See: https://inertiajs.com/history-encryption
Example:
from litestar_vite.inertia import clear_history @post("/logout") async def logout(request: Request) -> InertiaRedirect: request.session.clear() clear_history(request) return InertiaRedirect(request, redirect_to="/login")
- litestar_vite.inertia.helpers.is_pagination_container(value: Any) bool[source]¶
Check if a value is a pagination container.
Detects common pagination types from Litestar and Advanced Alchemy: - litestar.pagination.OffsetPagination (items, limit, offset, total) - litestar.pagination.ClassicPagination (items, page_size, current_page, total_pages) - advanced_alchemy.service.OffsetPagination
Also supports any object with an items attribute and pagination metadata.
- Parameters:
value¶ – The value to check.
- Returns:
True if value appears to be a pagination container.
- litestar_vite.inertia.helpers.extract_pagination_scroll_props(value: Any, page_param: str = 'page') tuple[Any, ScrollPropsConfig | None][source]¶
Extract items and scroll props from a pagination container.
For OffsetPagination, calculates page numbers from limit/offset/total. For ClassicPagination, uses current_page/total_pages directly.
- Parameters:
- Returns:
A tuple of (items, scroll_props) where scroll_props is None if value is not a pagination container.
Example:
items, scroll = extract_pagination_scroll_props(pagination) For OffsetPagination with limit=10, offset=20, total=50 the resulting scroll props are: ScrollPropsConfig(current_page=3, previous_page=2, next_page=4).
- litestar_vite.inertia.helpers.pagination_to_dict(value: Any) dict[str, Any][source]¶
Convert a pagination container to a dict with items and all metadata.
Dynamically extracts known pagination attributes from any pagination container class. This supports custom pagination implementations as long as they have an
itemsattribute and standard pagination metadata.The function checks for common pagination attributes like
total,limit,offset(for offset pagination),page_size,current_page,total_pages(for classic pagination), and cursor-based attributes. Any found attributes are included in the result dict with camelCase keys.- Parameters:
value¶ – A pagination container with
itemsand metadata attributes.- Returns:
A dict with
itemsand any found pagination metadata (camelCase keys).
Example:
from litestar.pagination import OffsetPagination pagination = OffsetPagination(items=[1, 2, 3], limit=10, offset=0, total=50) result = pagination_to_dict(pagination) The result contains {"items": [1, 2, 3], "total": 50, "limit": 10, "offset": 0}.
Note
This function is used internally by InertiaResponse to preserve pagination metadata when returning pagination containers from routes.