diff --git a/packages/next-intl/.size-limit.ts b/packages/next-intl/.size-limit.ts
index 71ddc43f7..18530be5b 100644
--- a/packages/next-intl/.size-limit.ts
+++ b/packages/next-intl/.size-limit.ts
@@ -15,7 +15,7 @@ const config: SizeLimitConfig = [
{
name: "import * from 'next-intl' (react-server)",
path: 'dist/esm/production/index.react-server.js',
- limit: '14.095 KB'
+ limit: '14.105 KB'
},
{
name: "import {createNavigation} from 'next-intl/navigation' (react-client)",
diff --git a/packages/next-intl/src/navigation/createNavigation.test.tsx b/packages/next-intl/src/navigation/createNavigation.test.tsx
index 97d2c1409..a83b38387 100644
--- a/packages/next-intl/src/navigation/createNavigation.test.tsx
+++ b/packages/next-intl/src/navigation/createNavigation.test.tsx
@@ -560,6 +560,24 @@ describe.each([
);
});
+ it("doesn't double-encode already encoded params", () => {
+ const markup = renderToString(
+
+ Create
+
+ );
+ expect(markup).toContain('href="/en/news/launch%20%2F%20party-3"');
+ });
+
it('handles relative pathnames', () => {
// @ts-expect-error -- Validation is still on
const markup = renderToString(Test);
diff --git a/packages/next-intl/src/navigation/shared/utils.tsx b/packages/next-intl/src/navigation/shared/utils.tsx
index 8b44c71e1..5ce4e5fc9 100644
--- a/packages/next-intl/src/navigation/shared/utils.tsx
+++ b/packages/next-intl/src/navigation/shared/utils.tsx
@@ -206,10 +206,7 @@ function encodePathname(pathname: string) {
//
// Therefore, the bottom line is that next-intl should take care of encoding non-ASCII
// characters in all cases, but can rely on `new URL()` to not double-encode characters.
- return pathname
- .split('/')
- .map((segment) => encodeURIComponent(segment))
- .join('/');
+ return new URL(pathname, 'http://l').pathname;
}
export function getRoute(