Skip to content

fix: Handle periods in dynamic route segments for production mode#6129

Open
timon0305 wants to merge 2 commits intoreflex-dev:mainfrom
timon0305:feature/fix-periods-in-dynamic-routes
Open

fix: Handle periods in dynamic route segments for production mode#6129
timon0305 wants to merge 2 commits intoreflex-dev:mainfrom
timon0305:feature/fix-periods-in-dynamic-routes

Conversation

@timon0305
Copy link

Summary

Fixes dynamic routes with periods (e.g., /data/v1.2.3/info) breaking in production mode. sirv treats URLs with extensions as asset requests, returning 404 instead of falling back to the SPA handler.

Changes

Example

For routes /data/[version]/info and /posts/[slug], the generated package.json now includes:

"prod": "sirv ./build/client --single 404.html --host --ignores '^/data/' --ignores '^/posts/'"

Test plan

  • All 27 route unit tests pass
  • 2589 unit tests pass
  • Manual testing with test app confirmed /data/v1.2.3/info works in production mode

Fixes #5803

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 12, 2026

Greptile Overview

Greptile Summary

This PR fixes dynamic routes with periods (e.g., /data/v1.2.3/info) breaking in production mode by adding sirv --ignores flags to exclude dynamic route prefixes from asset detection.

Key Changes:

  • Added get_dynamic_route_prefixes() in reflex/route.py:135 to extract route patterns with dynamic segments
  • Updated _compile_package_json() in reflex/utils/frontend_skeleton.py:170 to build sirv command with --ignores flags
  • Wired route collection in reflex/app.py:1540 during compilation to generate and pass dynamic route prefixes

Issues Found:

  • The catchall pattern detection in reflex/route.py:161-165 is incomplete - missing checks for STRICT_CATCHALL ([...slug]) and OPTIONAL_CATCHALL ([[...slug]]) patterns, only checking for the literal "[[...splat]]" string

Confidence Score: 4/5

  • This PR is safe to merge after fixing the catchall pattern detection
  • The implementation correctly solves the stated problem of periods in dynamic routes, and the approach of using sirv --ignores is sound. However, there's a logical bug where catchall route patterns ([...slug] and [[...slug]]) won't be properly detected, meaning routes using those patterns may still break. The fix is straightforward - add the missing regex checks.
  • Pay close attention to reflex/route.py - the catchall pattern logic needs to be fixed before merging

Important Files Changed

Filename Overview
reflex/route.py Added get_dynamic_route_prefixes() to extract route patterns for sirv --ignores, but missing checks for catchall patterns ([...slug] and [[...slug]])
reflex/utils/frontend_skeleton.py Updated _compile_package_json() and initialize_package_json() to accept dynamic route prefixes and append them as --ignores flags to sirv command
reflex/app.py Wired route collection during compilation to generate sirv --ignores flags by calling get_dynamic_route_prefixes() and passing to initialize_package_json()

Sequence Diagram

sequenceDiagram
    participant App as App.compile()
    participant Route as route.get_dynamic_route_prefixes()
    participant Skeleton as frontend_skeleton.initialize_package_json()
    participant PackageJson as package.json

    App->>App: Collect routes from _unevaluated_pages
    App->>Route: get_dynamic_route_prefixes(routes)
    Route->>Route: Filter routes with dynamic segments
    Route->>Route: Extract prefix before first dynamic segment
    Route-->>App: Return list of prefixes (e.g., ["^/data/", "^/posts/"])
    
    alt dynamic_route_prefixes exist
        App->>Skeleton: initialize_package_json(dynamic_route_prefixes)
        Skeleton->>Skeleton: Build sirv command with --ignores flags
        Skeleton->>PackageJson: Write package.json with updated prod command
    end
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Copy link
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool fix, but it doesn't work for me

prod_command = constants.PackageJson.Commands.PROD
if dynamic_route_prefixes:
# Add --ignores flag for each dynamic route prefix
ignores = " ".join(f"--ignores '{prefix}'" for prefix in dynamic_route_prefixes)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use shlex.join instead so these values get properly shell quoted

prefix = "/" + prefix
if prefix and prefix != "/":
# Add trailing slash and anchor to start for sirv pattern
pattern = f"^{prefix}/"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should use re.escape(prefix) here to ensure the prefix will never contain regex injection

@masenf
Copy link
Collaborator

masenf commented Feb 13, 2026

is this even a bug anymore? on current main:

import reflex as rx


def index():
    return rx.heading(rx.State.id, rx.State.version, rx.State.slug)


app = rx.App()
app.add_page(index, route="/foo/[id]/[version]/[slug]")
app.add_page(index, route="/data/[version]/info")
app.add_page(index, route="/posts/[slug]")
app.add_page(index, route="/[id]")
app.add_page(index)

all of /data/v1.2.3/info and various combinations of periods are rendering correctly. the /[id] one does not work with periods, but that's probably okay.

maybe the bug got fixed upstream?

either way, when i run with the --ignores arguments from this PR, the routes below them don't seem to render at all

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't have periods '.' in dynamic routes in prod because they break sirv

2 participants