diff --git a/e2e/solid-router/basic-file-based/src/routeTree.gen.ts b/e2e/solid-router/basic-file-based/src/routeTree.gen.ts
index d9f29a8451f..24254a6227a 100644
--- a/e2e/solid-router/basic-file-based/src/routeTree.gen.ts
+++ b/e2e/solid-router/basic-file-based/src/routeTree.gen.ts
@@ -9,6 +9,7 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
+import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국'
import { Route as RemountDepsRouteImport } from './routes/remountDeps'
import { Route as PostsRouteImport } from './routes/posts'
import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps'
@@ -88,6 +89,12 @@ import { Route as RelativeLinkPathPathIndexRouteImport } from './routes/relative
import { Route as RelativeLinkNestedDeepIndexRouteImport } from './routes/relative/link/nested/deep/index'
import { Route as ParamsPsNamedFooBarBazRouteImport } from './routes/params-ps/named/$foo/$bar.$baz'
+const Char45824Char54620Char48124Char44397Route =
+ Char45824Char54620Char48124Char44397RouteImport.update({
+ id: '/대한민국',
+ path: '/대한민국',
+ getParentRoute: () => rootRouteImport,
+ } as any)
const RemountDepsRoute = RemountDepsRouteImport.update({
id: '/remountDeps',
path: '/remountDeps',
@@ -514,6 +521,7 @@ export interface FileRoutesByFullPath {
'/notRemountDeps': typeof NotRemountDepsRoute
'/posts': typeof PostsRouteWithChildren
'/remountDeps': typeof RemountDepsRoute
+ '/대한민국': typeof Char45824Char54620Char48124Char44397Route
'/non-nested/named': typeof NonNestedNamedRouteRouteWithChildren
'/non-nested/path': typeof NonNestedPathRouteRouteWithChildren
'/non-nested/prefix': typeof NonNestedPrefixRouteRouteWithChildren
@@ -589,6 +597,7 @@ export interface FileRoutesByTo {
'/editing-b': typeof EditingBRoute
'/notRemountDeps': typeof NotRemountDepsRoute
'/remountDeps': typeof RemountDepsRoute
+ '/대한민국': typeof Char45824Char54620Char48124Char44397Route
'/non-nested/named': typeof NonNestedNamedRouteRouteWithChildren
'/non-nested/path': typeof NonNestedPathRouteRouteWithChildren
'/non-nested/prefix': typeof NonNestedPrefixRouteRouteWithChildren
@@ -663,6 +672,7 @@ export interface FileRoutesById {
'/notRemountDeps': typeof NotRemountDepsRoute
'/posts': typeof PostsRouteWithChildren
'/remountDeps': typeof RemountDepsRoute
+ '/대한민국': typeof Char45824Char54620Char48124Char44397Route
'/non-nested/named': typeof NonNestedNamedRouteRouteWithChildren
'/non-nested/path': typeof NonNestedPathRouteRouteWithChildren
'/non-nested/prefix': typeof NonNestedPrefixRouteRouteWithChildren
@@ -744,6 +754,7 @@ export interface FileRouteTypes {
| '/notRemountDeps'
| '/posts'
| '/remountDeps'
+ | '/대한민국'
| '/non-nested/named'
| '/non-nested/path'
| '/non-nested/prefix'
@@ -819,6 +830,7 @@ export interface FileRouteTypes {
| '/editing-b'
| '/notRemountDeps'
| '/remountDeps'
+ | '/대한민국'
| '/non-nested/named'
| '/non-nested/path'
| '/non-nested/prefix'
@@ -892,6 +904,7 @@ export interface FileRouteTypes {
| '/notRemountDeps'
| '/posts'
| '/remountDeps'
+ | '/대한민국'
| '/non-nested/named'
| '/non-nested/path'
| '/non-nested/prefix'
@@ -973,6 +986,7 @@ export interface RootRouteChildren {
NotRemountDepsRoute: typeof NotRemountDepsRoute
PostsRoute: typeof PostsRouteWithChildren
RemountDepsRoute: typeof RemountDepsRoute
+ Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route
ParamsPsNonNestedRouteRoute: typeof ParamsPsNonNestedRouteRouteWithChildren
RelativeLinkRouteRoute: typeof RelativeLinkRouteRouteWithChildren
RelativeUseNavigateRouteRoute: typeof RelativeUseNavigateRouteRouteWithChildren
@@ -995,6 +1009,13 @@ export interface RootRouteChildren {
declare module '@tanstack/solid-router' {
interface FileRoutesByPath {
+ '/대한민국': {
+ id: '/대한민국'
+ path: '/대한민국'
+ fullPath: '/대한민국'
+ preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport
+ parentRoute: typeof rootRouteImport
+ }
'/remountDeps': {
id: '/remountDeps'
path: '/remountDeps'
@@ -1884,6 +1905,8 @@ const rootRouteChildren: RootRouteChildren = {
NotRemountDepsRoute: NotRemountDepsRoute,
PostsRoute: PostsRouteWithChildren,
RemountDepsRoute: RemountDepsRoute,
+ Char45824Char54620Char48124Char44397Route:
+ Char45824Char54620Char48124Char44397Route,
ParamsPsNonNestedRouteRoute: ParamsPsNonNestedRouteRouteWithChildren,
RelativeLinkRouteRoute: RelativeLinkRouteRouteWithChildren,
RelativeUseNavigateRouteRoute: RelativeUseNavigateRouteRouteWithChildren,
diff --git "a/e2e/solid-router/basic-file-based/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx" "b/e2e/solid-router/basic-file-based/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx"
new file mode 100644
index 00000000000..897c0576cc4
--- /dev/null
+++ "b/e2e/solid-router/basic-file-based/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx"
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/대한민국')({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ return
Hello "/대한민국"!
+}
diff --git a/e2e/solid-router/basic/src/main.tsx b/e2e/solid-router/basic/src/main.tsx
index 9bb8b6f8e4a..4005b95067f 100644
--- a/e2e/solid-router/basic/src/main.tsx
+++ b/e2e/solid-router/basic/src/main.tsx
@@ -8,6 +8,7 @@ import {
createRootRoute,
createRoute,
createRouter,
+ redirect,
} from '@tanstack/solid-router'
import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
import { NotFoundError, fetchPost, fetchPosts } from './posts'
@@ -207,11 +208,213 @@ function LayoutBComponent() {
return I'm layout B!
}
+const paramsPsRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: '/params-ps',
+})
+
+const paramsPsIndexRoute = createRoute({
+ getParentRoute: () => paramsPsRoute,
+ path: '/',
+ component: function ParamsIndex() {
+ return (
+
+
Named path params
+
+ -
+
+ /params-ps/named/$foo
+
+
+ -
+
+ /params-ps/named/{'prefix{$foo}'}
+
+
+ -
+
+ /params-ps/named/{'{$foo}suffix'}
+
+
+
+
+
Wildcard path params
+
+ -
+
+ /params-ps/wildcard/$
+
+
+ -
+
+ /params-ps/wildcard/{'prefix{$}'}
+
+
+ -
+
+ /params-ps/wildcard/{'{$}suffix'}
+
+
+
+
+ )
+ },
+})
+
+const paramsPsNamedRoute = createRoute({
+ getParentRoute: () => paramsPsRoute,
+ path: '/named',
+})
+
+const paramsPsNamedIndexRoute = createRoute({
+ getParentRoute: () => paramsPsNamedRoute,
+ path: '/',
+ beforeLoad: () => {
+ throw redirect({ to: '/params-ps' })
+ },
+})
+
+const paramsPsNamedFooRoute = createRoute({
+ getParentRoute: () => paramsPsNamedRoute,
+ path: '/$foo',
+ component: function ParamsNamedFoo() {
+ const p = paramsPsNamedFooRoute.useParams()
+ return (
+
+
ParamsNamedFoo
+
{JSON.stringify(p())}
+
+ )
+ },
+})
+
+const paramsPsNamedFooPrefixRoute = createRoute({
+ getParentRoute: () => paramsPsNamedRoute,
+ path: '/prefix{$foo}',
+ component: function ParamsNamedFooMarkdown() {
+ const p = paramsPsNamedFooPrefixRoute.useParams()
+ return (
+
+
ParamsNamedFooPrefix
+
{JSON.stringify(p())}
+
+ )
+ },
+})
+
+const paramsPsNamedFooSuffixRoute = createRoute({
+ getParentRoute: () => paramsPsNamedRoute,
+ path: '/{$foo}suffix',
+ component: function ParamsNamedFooSuffix() {
+ const p = paramsPsNamedFooSuffixRoute.useParams()
+ return (
+
+
ParamsNamedFooSuffix
+
{JSON.stringify(p())}
+
+ )
+ },
+})
+
+const paramsPsWildcardRoute = createRoute({
+ getParentRoute: () => paramsPsRoute,
+ path: '/wildcard',
+})
+
+const paramsPsWildcardIndexRoute = createRoute({
+ getParentRoute: () => paramsPsWildcardRoute,
+ path: '/',
+ beforeLoad: () => {
+ throw redirect({ to: '/params-ps' })
+ },
+})
+
+const paramsPsWildcardSplatRoute = createRoute({
+ getParentRoute: () => paramsPsWildcardRoute,
+ path: '$',
+ component: function ParamsWildcardSplat() {
+ const p = paramsPsWildcardSplatRoute.useParams()
+ return (
+
+
ParamsWildcardSplat
+
{JSON.stringify(p())}
+
+ )
+ },
+})
+
+const paramsPsWildcardSplatPrefixRoute = createRoute({
+ getParentRoute: () => paramsPsWildcardRoute,
+ path: 'prefix{$}',
+ component: function ParamsWildcardSplatPrefix() {
+ const p = paramsPsWildcardSplatPrefixRoute.useParams()
+ return (
+
+
ParamsWildcardSplatPrefix
+
{JSON.stringify(p())}
+
+ )
+ },
+})
+
+const paramsPsWildcardSplatSuffixRoute = createRoute({
+ getParentRoute: () => paramsPsWildcardRoute,
+ path: '{$}suffix',
+ component: function ParamsWildcardSplatSuffix() {
+ const p = paramsPsWildcardSplatSuffixRoute.useParams()
+ return (
+
+
ParamsWildcardSplatSuffix
+
{JSON.stringify(p())}
+
+ )
+ },
+})
+
const routeTree = rootRoute.addChildren([
postsRoute.addChildren([postRoute, postsIndexRoute]),
layoutRoute.addChildren([
layout2Route.addChildren([layoutARoute, layoutBRoute]),
]),
+ paramsPsRoute.addChildren([
+ paramsPsNamedRoute.addChildren([
+ paramsPsNamedFooPrefixRoute,
+ paramsPsNamedFooSuffixRoute,
+ paramsPsNamedFooRoute,
+ paramsPsNamedIndexRoute,
+ ]),
+ paramsPsWildcardRoute.addChildren([
+ paramsPsWildcardSplatRoute,
+ paramsPsWildcardSplatPrefixRoute,
+ paramsPsWildcardSplatSuffixRoute,
+ paramsPsWildcardIndexRoute,
+ ]),
+ paramsPsIndexRoute,
+ ]),
indexRoute,
])
diff --git a/e2e/solid-router/basic/tests/params.spec.ts b/e2e/solid-router/basic/tests/params.spec.ts
new file mode 100644
index 00000000000..ac18f39864a
--- /dev/null
+++ b/e2e/solid-router/basic/tests/params.spec.ts
@@ -0,0 +1,149 @@
+import { expect, test } from '@playwright/test'
+
+test.describe('params operations + prefix/suffix', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/params-ps')
+ })
+
+ test.describe('named params', () => {
+ const NAMED_PARAMS_PAIRS = [
+ // Test ID | Expected href
+ {
+ id: 'l-to-named-foo',
+ pathname: '/params-ps/named/foo',
+ params: { foo: 'foo' },
+ destHeadingId: 'ParamsNamedFoo',
+ },
+ {
+ id: 'l-to-named-prefixfoo',
+ pathname: '/params-ps/named/prefixfoo',
+ params: { foo: 'foo' },
+ destHeadingId: 'ParamsNamedFooPrefix',
+ },
+ {
+ id: 'l-to-named-foosuffix',
+ pathname: '/params-ps/named/foosuffix',
+ params: { foo: 'foo' },
+ destHeadingId: 'ParamsNamedFooSuffix',
+ },
+ ] satisfies Array<{
+ id: string
+ pathname: string
+ params: Record
+ destHeadingId: string
+ }>
+
+ test.describe('Link', () => {
+ NAMED_PARAMS_PAIRS.forEach(({ id, pathname }) => {
+ test(`interpolation for testid="${id}" has href="${pathname}"`, async ({
+ page,
+ }) => {
+ const link = page.getByTestId(id)
+ await expect(link).toHaveAttribute('href', pathname)
+ })
+ })
+
+ NAMED_PARAMS_PAIRS.forEach(({ id, pathname }) => {
+ test(`navigation for testid="${id}" succeeds to href="${pathname}"`, async ({
+ page,
+ }) => {
+ const link = page.getByTestId(id)
+ await link.click()
+ await page.waitForLoadState('networkidle')
+ const pagePathname = new URL(page.url()).pathname
+ expect(pagePathname).toBe(pathname)
+ })
+ })
+ })
+
+ NAMED_PARAMS_PAIRS.forEach(({ pathname, params, destHeadingId }) => {
+ test(`on first-load to "${pathname}" has correct params`, async ({
+ page,
+ }) => {
+ await page.goto(pathname)
+ await page.waitForLoadState('networkidle')
+ const pagePathname = new URL(page.url()).pathname
+ expect(pagePathname).toBe(pathname)
+
+ const headingEl = page.getByRole('heading', { name: destHeadingId })
+ await expect(headingEl).toBeVisible()
+
+ const paramsEl = page.getByTestId('params-output')
+ const paramsText = await paramsEl.innerText()
+ const paramsObj = JSON.parse(paramsText)
+ expect(paramsObj).toEqual(params)
+ })
+ })
+ })
+
+ test.describe('wildcard param', () => {
+ const WILDCARD_PARAM_PAIRS = [
+ // Test ID | Expected href
+ {
+ id: 'l-to-wildcard-foo',
+ pathname: '/params-ps/wildcard/foo',
+ params: { '*': 'foo', _splat: 'foo' },
+ destHeadingId: 'ParamsWildcardSplat',
+ },
+ {
+ id: 'l-to-wildcard-prefixfoo',
+ pathname: '/params-ps/wildcard/prefixfoo',
+ params: { '*': 'foo', _splat: 'foo' },
+ destHeadingId: 'ParamsWildcardSplatPrefix',
+ },
+ {
+ id: 'l-to-wildcard-foosuffix',
+ pathname: '/params-ps/wildcard/foosuffix',
+ params: { '*': 'foo', _splat: 'foo' },
+ destHeadingId: 'ParamsWildcardSplatSuffix',
+ },
+ ] satisfies Array<{
+ id: string
+ pathname: string
+ params: Record
+ destHeadingId: string
+ }>
+
+ test.describe('Link', () => {
+ WILDCARD_PARAM_PAIRS.forEach(({ id, pathname }) => {
+ test(`interpolation for testid="${id}" has href="${pathname}"`, async ({
+ page,
+ }) => {
+ const link = page.getByTestId(id)
+ await expect(link).toHaveAttribute('href', pathname)
+ })
+ })
+
+ WILDCARD_PARAM_PAIRS.forEach(({ id, pathname }) => {
+ test(`navigation for testid="${id}" succeeds to href="${pathname}"`, async ({
+ page,
+ }) => {
+ const link = page.getByTestId(id)
+ await link.click()
+ await page.waitForLoadState('networkidle')
+ const pagePathname = new URL(page.url()).pathname
+ expect(pagePathname).toBe(pathname)
+ })
+ })
+ })
+
+ WILDCARD_PARAM_PAIRS.forEach(({ pathname, params, destHeadingId }) => {
+ test(`on first-load to "${pathname}" has correct params`, async ({
+ page,
+ }) => {
+ await page.goto(pathname)
+ await page.waitForLoadState('networkidle')
+ const pagePathname = new URL(page.url()).pathname
+ expect(pagePathname).toBe(pathname)
+
+ const headingEl = page.getByRole('heading', { name: destHeadingId })
+ await expect(headingEl).toBeVisible()
+
+ const paramsEl = page.getByTestId('params-output')
+ const paramsText = await paramsEl.innerText()
+ const paramsObj = JSON.parse(paramsText)
+ expect(paramsObj).toEqual(params)
+ })
+ })
+ })
+})
diff --git a/e2e/solid-start/basic/src/routeTree.gen.ts b/e2e/solid-start/basic/src/routeTree.gen.ts
index 9bbb8755f9c..aac25901997 100644
--- a/e2e/solid-start/basic/src/routeTree.gen.ts
+++ b/e2e/solid-start/basic/src/routeTree.gen.ts
@@ -9,6 +9,7 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
+import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국'
import { Route as UsersRouteImport } from './routes/users'
import { Route as StreamRouteImport } from './routes/stream'
import { Route as ScriptsRouteImport } from './routes/scripts'
@@ -46,6 +47,12 @@ import { Route as RedirectTargetServerFnViaUseServerFnRouteImport } from './rout
import { Route as RedirectTargetServerFnViaLoaderRouteImport } from './routes/redirect/$target/serverFn/via-loader'
import { Route as RedirectTargetServerFnViaBeforeLoadRouteImport } from './routes/redirect/$target/serverFn/via-beforeLoad'
+const Char45824Char54620Char48124Char44397Route =
+ Char45824Char54620Char48124Char44397RouteImport.update({
+ id: '/대한민국',
+ path: '/대한민국',
+ getParentRoute: () => rootRouteImport,
+ } as any)
const UsersRoute = UsersRouteImport.update({
id: '/users',
path: '/users',
@@ -242,6 +249,7 @@ export interface FileRoutesByFullPath {
'/scripts': typeof ScriptsRoute
'/stream': typeof StreamRoute
'/users': typeof UsersRouteWithChildren
+ '/대한민국': typeof Char45824Char54620Char48124Char44397Route
'/api/users': typeof ApiUsersRouteWithChildren
'/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute
'/not-found/via-loader': typeof NotFoundViaLoaderRoute
@@ -274,6 +282,7 @@ export interface FileRoutesByTo {
'/links': typeof LinksRoute
'/scripts': typeof ScriptsRoute
'/stream': typeof StreamRoute
+ '/대한민국': typeof Char45824Char54620Char48124Char44397Route
'/api/users': typeof ApiUsersRouteWithChildren
'/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute
'/not-found/via-loader': typeof NotFoundViaLoaderRoute
@@ -311,6 +320,7 @@ export interface FileRoutesById {
'/scripts': typeof ScriptsRoute
'/stream': typeof StreamRoute
'/users': typeof UsersRouteWithChildren
+ '/대한민국': typeof Char45824Char54620Char48124Char44397Route
'/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren
'/api/users': typeof ApiUsersRouteWithChildren
'/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute
@@ -350,6 +360,7 @@ export interface FileRouteTypes {
| '/scripts'
| '/stream'
| '/users'
+ | '/대한민국'
| '/api/users'
| '/not-found/via-beforeLoad'
| '/not-found/via-loader'
@@ -382,6 +393,7 @@ export interface FileRouteTypes {
| '/links'
| '/scripts'
| '/stream'
+ | '/대한민국'
| '/api/users'
| '/not-found/via-beforeLoad'
| '/not-found/via-loader'
@@ -418,6 +430,7 @@ export interface FileRouteTypes {
| '/scripts'
| '/stream'
| '/users'
+ | '/대한민국'
| '/_layout/_layout-2'
| '/api/users'
| '/not-found/via-beforeLoad'
@@ -457,6 +470,7 @@ export interface RootRouteChildren {
ScriptsRoute: typeof ScriptsRoute
StreamRoute: typeof StreamRoute
UsersRoute: typeof UsersRouteWithChildren
+ Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route
ApiUsersRoute: typeof ApiUsersRouteWithChildren
RedirectTargetRoute: typeof RedirectTargetRouteWithChildren
RedirectIndexRoute: typeof RedirectIndexRoute
@@ -465,6 +479,13 @@ export interface RootRouteChildren {
declare module '@tanstack/solid-router' {
interface FileRoutesByPath {
+ '/대한민국': {
+ id: '/대한민국'
+ path: '/대한민국'
+ fullPath: '/대한민국'
+ preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport
+ parentRoute: typeof rootRouteImport
+ }
'/users': {
id: '/users'
path: '/users'
@@ -850,6 +871,8 @@ const rootRouteChildren: RootRouteChildren = {
ScriptsRoute: ScriptsRoute,
StreamRoute: StreamRoute,
UsersRoute: UsersRouteWithChildren,
+ Char45824Char54620Char48124Char44397Route:
+ Char45824Char54620Char48124Char44397Route,
ApiUsersRoute: ApiUsersRouteWithChildren,
RedirectTargetRoute: RedirectTargetRouteWithChildren,
RedirectIndexRoute: RedirectIndexRoute,
diff --git "a/e2e/solid-start/basic/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx" "b/e2e/solid-start/basic/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx"
new file mode 100644
index 00000000000..897c0576cc4
--- /dev/null
+++ "b/e2e/solid-start/basic/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx"
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/solid-router'
+
+export const Route = createFileRoute('/대한민국')({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ return Hello "/대한민국"!
+}
diff --git a/e2e/solid-start/basic/tests/params.spec.ts b/e2e/solid-start/basic/tests/params.spec.ts
new file mode 100644
index 00000000000..737c82f35d2
--- /dev/null
+++ b/e2e/solid-start/basic/tests/params.spec.ts
@@ -0,0 +1,22 @@
+import { expect } from '@playwright/test'
+
+import { test } from '@tanstack/router-e2e-utils'
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('/')
+})
+
+test.use({
+ whitelistErrors: [
+ /Failed to load resource: the server responded with a status of 404/,
+ ],
+})
+test.describe('Unicode route rendering', () => {
+ test('should render non-latin route correctly', async ({ page, baseURL }) => {
+ await page.goto('/대한민국')
+
+ await expect(page.locator('body')).toContainText('Hello "/대한민국"!')
+
+ expect(page.url()).toBe(`${baseURL}/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD`)
+ })
+})