diff --git a/e2e/react-start/basic/src/routes/encoding/link-active/$target.tsx b/e2e/react-start/basic/src/routes/encoding/link-active/$target.tsx
new file mode 100644
index 00000000000..f13637d2344
--- /dev/null
+++ b/e2e/react-start/basic/src/routes/encoding/link-active/$target.tsx
@@ -0,0 +1,24 @@
+import { createFileRoute, Link } from '@tanstack/react-router'
+import z from 'zod'
+
+export const Route = createFileRoute('/encoding/link-active/$target')({
+ validateSearch: z.object({ foo: z.string().optional() }),
+
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ return (
+
+ link to self with $target={Route.useParams().target}, search.foo=
+ {Route.useSearch().foo}
+
+ )
+}
diff --git a/e2e/react-start/basic/src/routes/encoding/link-active/index.tsx b/e2e/react-start/basic/src/routes/encoding/link-active/index.tsx
new file mode 100644
index 00000000000..1b8cc127024
--- /dev/null
+++ b/e2e/react-start/basic/src/routes/encoding/link-active/index.tsx
@@ -0,0 +1,19 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/encoding/link-active/')({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ return (
+
+
+ link to $target=ê
+
+
+
+ link to $target=hello
+
+
+ )
+}
diff --git a/e2e/react-start/basic/tests/encoding.spec.ts b/e2e/react-start/basic/tests/encoding.spec.ts
new file mode 100644
index 00000000000..169cf4c7025
--- /dev/null
+++ b/e2e/react-start/basic/tests/encoding.spec.ts
@@ -0,0 +1,47 @@
+import { expect } from '@playwright/test'
+
+import { test } from '@tanstack/router-e2e-utils'
+import combinateImport from 'combinate'
+
+// somehow playwright does not correctly import default exports
+const combinate = (combinateImport as any).default as typeof combinateImport
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('/')
+})
+
+test.use({
+ whitelistErrors: [
+ /Failed to load resource: the server responded with a status of 404/,
+ ],
+})
+
+combinate
+test.describe('link active', () => {
+ const testMatrix = combinate({
+ target: ['ê', 'hello'] as const,
+ search: [undefined, { foo: 'bar' }, { foo: 'ö' }] as const,
+ })
+
+ testMatrix.forEach(({ target, search }) => {
+ test(`should correctly highlight active link for $target=${target} and search=${JSON.stringify(search)}`, async ({
+ page,
+ }) => {
+ const url = new URL(
+ `/encoding/link-active/${target}`,
+ 'http://localhost:3000',
+ )
+ if (search) {
+ Object.entries(search).forEach(([key, value]) => {
+ url.searchParams.set(key, value)
+ })
+ }
+ const hrefWithoutOrigin = url.href.replace(url.origin, '')
+ await page.goto(hrefWithoutOrigin)
+ const link = page.getByTestId('self-link')
+ await expect(link).toBeInViewport()
+ await expect(link).toHaveAttribute('class', 'font-bold')
+ await expect(link).toHaveAttribute('data-status', 'active')
+ })
+ })
+})