경로 매개변수는 하나의 세그먼트(다음 /까지의 텍스트)를 매칭하고 그 값을 이름이 지정된 변수로 제공합니다. 경로에서 $ 문자를 접두사로 사용한 뒤 값을 할당할 키 변수를 적어 정의합니다. 다음은 유효한 경로 매개변수 경로입니다.
$postId$name$teamIdabout/$nameteam/$teamIdblog/$postId
경로 매개변수 라우트는 다음 /까지만 매칭하므로 자식 라우트를 만들어 계층 구조를 계속 표현할 수 있습니다.
경로 매개변수로 게시물 ID를 매칭하는 게시물 라우트 파일을 만들어 봅니다.
React
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => {
return fetchPost(params.postId)
},
})
Solid
import { createFileRoute } from '@tanstack/solid-router'
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => {
return fetchPost(params.postId)
},
})
자식 라우트에서 경로 매개변수 사용
경로 매개변수를 파싱하면 모든 자식 라우트에서 사용할 수 있습니다. 즉, postRoute의 자식 라우트를 정의하면 자식 라우트의 경로에서 URL의 postId 변수를 사용할 수 있습니다.
로더의 경로 매개변수
경로 매개변수는 params 객체로 로더에 전달됩니다. 이 객체의 키는 경로 매개변수의 이름이고 값은 실제 URL 경로에서 파싱한 값입니다. 예를 들어 /blog/123 URL을 방문하면 params 객체는 { postId: '123' }가 됩니다.
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => {
return fetchPost(params.postId)
},
})
params 객체는 beforeLoad 옵션에도 전달됩니다.
export const Route = createFileRoute('/posts/$postId')({
beforeLoad: async ({ params }) => {
// do something with params.postId
},
})
컴포넌트의 경로 매개변수
postRoute에 컴포넌트를 추가하면 라우트의 useParams 훅을 사용해 URL의 postId 변수에 접근할 수 있습니다.
React
export const Route = createFileRoute('/posts/$postId')({
component: PostComponent,
})
function PostComponent() {
const { postId } = Route.useParams()
return <div>Post {postId}</div>
}
Solid
export const Route = createFileRoute('/posts/$postId')({
component: PostComponent,
})
function PostComponent() {
const params = Route.useParams()
return <div>Post {params().postId}</div>
}
🧠 간단한 팁: 컴포넌트가 코드 분할되어 있다면 getRouteApi 함수를 사용해 타입이 지정된
useParams()훅에 접근할 때Route구성을 가져오지 않아도 됩니다.
라우트 외부의 경로 매개변수
전역으로 내보낸 useParams 훅을 사용해 애플리케이션의 모든 컴포넌트에서 파싱된 경로 매개변수에 접근할 수도 있습니다. 모호한 위치에서 매개변수에 접근한다는 의미로 useParams에 strict: false 옵션을 전달해야 합니다.
React
function PostComponent() {
const { postId } = useParams({ strict: false })
return <div>Post {postId}</div>
}
Solid
function PostComponent() {
const params = useParams({ strict: false })
return <div>Post {params().postId}</div>
}
파싱된 경로 매개변수 라우트 우선순위 지정
여러 동적, 선택적 또는 와일드카드 라우트가 동일한 URL과 매칭될 수 있으면 params.parse가 있는 라우트를 해당 옵션이 없는 동일한 라우트보다 먼저 시도합니다. 여러 매칭 후보가 params.parse를 사용하면 params.priority를 사용해 어떤 후보를 먼저 시도할지 제어할 수 있습니다.
더 높은 params.priority 값이 먼저 시도됩니다. 기본 우선순위는 0이며, 우선순위가 더 높은 라우트의 params.parse가 false를 반환하면 다음 후보 라우트로 매칭을 계속합니다.
params.parse는 라우트 계획 중에 실행되며 한 번 이상 평가될 수 있습니다.
동일한 원시 매개변수에 대해 결정적이고 부수 효과가 없어야 합니다.
export const Route = createFileRoute('/posts/$postId')({
params: {
priority: 10,
parse: ({ postId }) => {
if (!/^\d+$/.test(postId)) return false
return { postId: Number(postId) }
},
stringify: ({ postId }) => ({ postId: String(postId) }),
},
})
대체 /posts/$slug 라우트가 있으면 /posts/123은 파싱된 숫자 라우트와 먼저 매칭될 수 있고, /posts/hello-world는 params.parse가 false를 반환할 때 slug 라우트로 넘어갈 수 있습니다.
params.priority는 params.parse를 사용하는 경쟁 후보에만 영향을 줍니다. 일반적인 라우트 구체성을 재정의하지 않으므로 정적 라우트는 여전히 동적, 선택적 또는 와일드카드 라우트보다 먼저 매칭됩니다.
경로 매개변수를 사용한 탐색
경로 매개변수가 있는 라우트로 탐색할 때 TypeScript는 매개변수를 객체로 전달하거나 매개변수 객체를 반환하는 함수로 전달하도록 요구합니다.
객체 방식은 다음과 같습니다.
function Component() {
return (
<Link to="/blog/$postId" params={{ postId: '123' }}>
Post 123
</Link>
)
}
함수 방식은 다음과 같습니다.
function Component() {
return (
<Link to="/blog/$postId" params={(prev) => ({ ...prev, postId: '123' })}>
Post 123
</Link>
)
}
함수 방식은 다른 라우트에서 URL에 이미 있는 매개변수를 유지해야 할 때 유용합니다. 함수 방식은 현재 매개변수를 인수로 받아 필요한 대로 수정한 후 최종 매개변수 객체를 반환할 수 있기 때문입니다.
경로 매개변수의 접두사 및 접미사
경로 매개변수에 접두사와 접미사를 사용해 더 복잡한 라우팅 패턴을 만들 수도 있습니다. 이렇게 하면 동적 세그먼트를 캡처하면서 특정 URL 구조와 매칭할 수 있습니다.
접두사나 접미사를 사용할 때는 경로 매개변수를 중괄호 {}로 감싸고 변수 이름 앞이나 뒤에 접두사 또는 접미사를 배치해 정의합니다.
접두사 정의
접두사는 변수 이름 앞의 중괄호 바깥에 접두사 텍스트를 배치해 정의합니다. 예를 들어 post-로 시작한 뒤 게시물 ID가 이어지는 URL과 매칭하려면 다음과 같이 정의합니다.
React
export const Route = createFileRoute('/posts/post-{$postId}')({
component: PostComponent,
})
function PostComponent() {
const { postId } = Route.useParams()
// postId will be the value after 'post-'
return <div>Post ID: {postId}</div>
}
Solid
export const Route = createFileRoute('/posts/post-{$postId}')({
component: PostComponent,
})
function PostComponent() {
const params = Route.useParams()
// postId will be the value after 'post-'
return <div>Post ID: {params().postId}</div>
}
접두사와 와일드카드 라우트를 결합해 더 복잡한 패턴을 만들 수도 있습니다.
React
export const Route = createFileRoute('/on-disk/storage-{$postId}/$')({
component: StorageComponent,
})
function StorageComponent() {
const { _splat } = Route.useParams()
// _splat, will be value after 'storage-'
// i.e. my-drive/documents/foo.txt
return <div>Storage Location: /{_splat}</div>
}
Solid
export const Route = createFileRoute('/on-disk/storage-{$postId}/$')({
component: StorageComponent,
})
function StorageComponent() {
const params = Route.useParams()
// _splat, will be value after 'storage-'
// i.e. my-drive/documents/foo.txt
return <div>Storage Location: /{params()._splat}</div>
}
접미사 정의
접미사는 변수 이름 뒤의 중괄호 바깥에 접미사 텍스트를 배치해 정의합니다. 예를 들어 txt로 끝나는 파일 이름 URL과 매칭하려면 다음과 같이 정의합니다.
React
export const Route = createFileRoute('/files/{$fileName}.txt')({
component: FileComponent,
})
function FileComponent() {
const { fileName } = Route.useParams()
// fileName will be the value before 'txt'
return <div>File Name: {fileName}</div>
}
Solid
export const Route = createFileRoute('/files/{$fileName}.txt')({
component: FileComponent,
})
function FileComponent() {
const params = Route.useParams()
// fileName will be the value before 'txt'
return <div>File Name: {params().fileName}</div>
}
접미사와 와일드카드를 결합해 더 복잡한 라우팅 패턴을 만들 수도 있습니다.
React
export const Route = createFileRoute('/files/{$}.txt')({
component: FileComponent,
})
function FileComponent() {
const { _splat } = Route.useParams()
// _splat will be the value before '.txt'
return <div>File Splat: {_splat}</div>
}
Solid
export const Route = createFileRoute('/files/{$}.txt')({
component: FileComponent,
})
function FileComponent() {
const params = Route.useParams()
// _splat will be the value before '.txt'
return <div>File Splat: {params()._splat}</div>
}
접두사와 접미사 결합
접두사와 접미사를 모두 결합해 매우 구체적인 라우팅 패턴을 만들 수 있습니다. 예를 들어 user-로 시작하고 .json으로 끝나는 URL과 매칭하려면 다음과 같이 정의합니다.
React
export const Route = createFileRoute('/users/user-{$userId}.json')({
component: UserComponent,
})
function UserComponent() {
const { userId } = Route.useParams()
// userId will be the value between 'user-' and '.json'
return <div>User ID: {userId}</div>
}
Solid
export const Route = createFileRoute('/users/user-{$userId}.json')({
component: UserComponent,
})
function UserComponent() {
const params = Route.useParams()
// userId will be the value between 'user-' and '.json'
return <div>User ID: {params().userId}</div>
}
앞의 예시와 마찬가지로 접두사 및 접미사와 함께 와일드카드를 사용할 수도 있습니다.
선택적 경로 매개변수
선택적 경로 매개변수를 사용하면 URL에 있을 수도 있고 없을 수도 있는 라우트 세그먼트를 정의할 수 있습니다. {-$paramName} 구문을 사용하며 특정 매개변수를 선택적으로 지정하는 유연한 라우팅 패턴을 제공합니다.
선택적 매개변수 정의
선택적 경로 매개변수는 대시 접두사가 있는 중괄호를 사용해 정의합니다: {-$paramName}
// Single optional parameter
// src/routes/posts/{-$category}.tsx
export const Route = createFileRoute('/posts/{-$category}')({
component: PostsComponent,
})
// Multiple optional parameters
// src/routes/posts/{-$category}/{-$slug}.tsx
export const Route = createFileRoute('/posts/{-$category}/{-$slug}')({
component: PostComponent,
})
// Mixed required and optional parameters
// src/routes/users/$id/{-$tab}.tsx
export const Route = createFileRoute('/users/$id/{-$tab}')({
component: UserComponent,
})
선택적 매개변수 작동 방식
선택적 매개변수는 유연한 URL 패턴을 만듭니다.
/posts/{-$category}는/posts와/posts/tech모두에 매칭됩니다./posts/{-$category}/{-$slug}는/posts,/posts/tech,/posts/tech/hello-world에 매칭됩니다./users/$id/{-$tab}은/users/123및/users/123/settings에 매칭됩니다.
선택적 매개변수가 URL에 없으면 라우트 핸들러와 컴포넌트에서 그 값은 undefined가 됩니다.
선택적 매개변수에 접근
선택적 매개변수는 컴포넌트에서 일반 매개변수와 동일하게 작동하지만 값이 undefined일 수 있습니다.
React
function PostsComponent() {
const { category } = Route.useParams()
return <div>{category ? `Posts in ${category}` : 'All Posts'}</div>
}
Solid
function PostsComponent() {
const params = Route.useParams()
return (
<div>
{params().category ? `Posts in ${params().category}` : 'All Posts'}
</div>
)
}
로더의 선택적 매개변수
선택적 매개변수는 로더에서 사용할 수 있으며 undefined일 수 있습니다.
export const Route = createFileRoute('/posts/{-$category}')({
loader: async ({ params }) => {
// params.category might be undefined
return fetchPosts({ category: params.category })
},
})
beforeLoad의 선택적 매개변수
선택적 매개변수는 beforeLoad 핸들러에서도 작동합니다.
export const Route = createFileRoute('/posts/{-$category}')({
beforeLoad: async ({ params }) => {
if (params.category) {
// Validate category exists
await validateCategory(params.category)
}
},
})
고급 선택적 매개변수 패턴
접두사 및 접미사 사용
선택적 매개변수는 접두사 및 접미사 패턴을 지원합니다.
React
// Route: /files/prefix{-$name}.txt
// Matches: /files/prefix.txt and /files/prefixdocument.txt
export const Route = createFileRoute('/files/prefix{-$name}.txt')({
component: FileComponent,
})
function FileComponent() {
const { name } = Route.useParams()
return <div>File: {name || 'default'}</div>
}
Solid
// Route: /files/prefix{-$name}.txt
// Matches: /files/prefix.txt and /files/prefixdocument.txt
export const Route = createFileRoute('/files/prefix{-$name}.txt')({
component: FileComponent,
})
function FileComponent() {
const params = Route.useParams()
return <div>File: {params().name || 'default'}</div>
}
모든 매개변수를 선택 사항으로 지정
모든 매개변수가 선택 사항인 라우트를 만들 수 있습니다.
React
// Route: /{-$year}/{-$month}/{-$day}
// Matches: /, /2023, /2023/12, /2023/12/25
export const Route = createFileRoute('/{-$year}/{-$month}/{-$day}')({
component: DateComponent,
})
function DateComponent() {
const { year, month, day } = Route.useParams()
if (!year) return <div>Select a year</div>
if (!month) return <div>Year: {year}</div>
if (!day)
return (
<div>
Month: {year}/{month}
</div>
)
return (
<div>
Date: {year}/{month}/{day}
</div>
)
}
Solid
// Route: /{-$year}/{-$month}/{-$day}
// Matches: /, /2023, /2023/12, /2023/12/25
export const Route = createFileRoute('/{-$year}/{-$month}/{-$day}')({
component: DateComponent,
})
function DateComponent() {
const params = Route.useParams()
if (!params().year) return <div>Select a year</div>
if (!params().month) return <div>Year: {params().year}</div>
if (!params().day)
return (
<div>
Month: {params().year}/{params().month}
</div>
)
return (
<div>
Date: {params().year}/{params().month}/{params().day}
</div>
)
}
와일드카드가 있는 선택적 매개변수
선택적 매개변수를 와일드카드와 결합해 복잡한 라우팅 패턴을 만들 수 있습니다.
React
// Route: /docs/v{-$version}/$
// Matches: /docs/extra/path, /docs/v2/extra/path
export const Route = createFileRoute('/docs/v{-$version}/$')({
component: DocsComponent,
})
function DocsComponent() {
const { version } = Route.useParams()
const { _splat } = Route.useParams()
return (
<div>
Version: {version ? `v${version}` : 'latest'}
Path: {_splat}
</div>
)
}
Solid
// Route: /docs/v{-$version}/$
// Matches: /docs/extra/path, /docs/v2/extra/path
export const Route = createFileRoute('/docs/v{-$version}/$')({
component: DocsComponent,
})
function DocsComponent() {
const params = Route.useParams()
return (
<div>
Version: {params().version ? `v${params().version}` : 'latest'}
Path: {params()._splat}
</div>
)
}
선택적 매개변수를 사용한 탐색
선택적 매개변수가 있는 라우트로 탐색할 때 포함할 매개변수를 세밀하게 제어할 수 있습니다.
function Navigation() {
return (
<div>
{/* Navigate with optional parameter */}
<Link to="/posts/{-$category}" params={{ category: 'tech' }}>
Tech Posts
</Link>
{/* Navigate without optional parameter */}
<Link to="/posts/{-$category}" params={{ category: undefined }}>
All Posts
</Link>
{/* Navigate with multiple optional parameters */}
<Link
to="/posts/{-$category}/{-$slug}"
params={{ category: 'tech', slug: 'react-tips' }}
>
Specific Post
</Link>
</div>
)
}
선택적 매개변수의 타입 안전성
TypeScript는 선택적 매개변수에 완전한 타입 안전성을 제공합니다.
React
function PostsComponent() {
// TypeScript knows category might be undefined
const { category } = Route.useParams() // category: string | undefined
// Safe navigation
const categoryUpper = category?.toUpperCase()
return <div>{categoryUpper || 'All Categories'}</div>
}
// Navigation is type-safe and flexible
<Link
to="/posts/{-$category}"
params={{ category: 'tech' }} // ✅ Valid - string
>
Tech Posts
</Link>
<Link
to="/posts/{-$category}"
params={{ category: 123 }} // ✅ Valid - number (auto-stringified)
>
Category 123
</Link>
Solid
function PostsComponent() {
// TypeScript knows category might be undefined
const params = Route.useParams() // category: string | undefined
// Safe navigation
const categoryUpper = params().category?.toUpperCase()
return <div>{categoryUpper || 'All Categories'}</div>
}
// Navigation is type-safe and flexible
<Link
to="/posts/{-$category}"
params={{ category: 'tech' }} // ✅ Valid - string
>
Tech Posts
</Link>
<Link
to="/posts/{-$category}"
params={{ category: 123 }} // ✅ Valid - number (auto-stringified)
>
Category 123
</Link>
선택적 경로 매개변수를 사용한 국제화(i18n)
선택적 경로 매개변수는 국제화(i18n) 라우팅 패턴을 구현하는 데 매우 적합합니다. 접두사 패턴을 사용하면 깔끔하고 SEO 친화적인 URL을 유지하면서 여러 언어를 처리할 수 있습니다.
접두사 기반 i18n
선택적 언어 접두사를 사용해 /en/about, /fr/about 또는 /about(기본 언어) 같은 URL을 지원합니다.
React
// Route: /{-$locale}/about
export const Route = createFileRoute('/{-$locale}/about')({
component: AboutComponent,
})
function AboutComponent() {
const { locale } = Route.useParams()
const currentLocale = locale || 'en' // Default to English
const content = {
en: { title: 'About Us', description: 'Learn more about our company.' },
fr: {
title: 'À Propos',
description: 'En savoir plus sur notre entreprise.',
},
es: {
title: 'Acerca de',
description: 'Conoce más sobre nuestra empresa.',
},
}
return (
<div>
<h1>{content[currentLocale]?.title}</h1>
<p>{content[currentLocale]?.description}</p>
</div>
)
}
Solid
// Route: /{-$locale}/about
export const Route = createFileRoute('/{-$locale}/about')({
component: AboutComponent,
})
function AboutComponent() {
const params = Route.useParams()
const currentLocale = params().locale || 'en' // Default to English
const content = {
en: { title: 'About Us', description: 'Learn more about our company.' },
fr: {
title: 'À Propos',
description: 'En savoir plus sur notre entreprise.',
},
es: {
title: 'Acerca de',
description: 'Conoce más sobre nuestra empresa.',
},
}
return (
<div>
<h1>{content[currentLocale]?.title}</h1>
<p>{content[currentLocale]?.description}</p>
</div>
)
}
이 패턴은 다음과 매칭됩니다.
/about(기본 로케일)/en/about(명시적 영어)/fr/about(프랑스어)/es/about(스페인어)
복잡한 i18n 패턴
선택적 매개변수를 결합해 더 정교한 i18n 라우팅을 구성합니다.
React
// Route: /{-$locale}/blog/{-$category}/$slug
export const Route = createFileRoute('/{-$locale}/blog/{-$category}/$slug')({
beforeLoad: async ({ params }) => {
const locale = params.locale || 'en'
const category = params.category
// Validate locale and category
const validLocales = ['en', 'fr', 'es', 'de']
if (locale && !validLocales.includes(locale)) {
throw new Error('Invalid locale')
}
return { locale, category }
},
loader: async ({ params, context }) => {
const { locale } = context
const { slug, category } = params
return fetchBlogPost({ slug, category, locale })
},
component: BlogPostComponent,
})
function BlogPostComponent() {
const { locale, category, slug } = Route.useParams()
const data = Route.useLoaderData()
return (
<article>
<h1>{data.title}</h1>
<p>
Category: {category || 'All'} | Language: {locale || 'en'}
</p>
<div>{data.content}</div>
</article>
)
}
Solid
// Route: /{-$locale}/blog/{-$category}/$slug
export const Route = createFileRoute('/{-$locale}/blog/{-$category}/$slug')({
beforeLoad: async ({ params }) => {
const locale = params.locale || 'en'
const category = params.category
// Validate locale and category
const validLocales = ['en', 'fr', 'es', 'de']
if (locale && !validLocales.includes(locale)) {
throw new Error('Invalid locale')
}
return { locale, category }
},
loader: async ({ params, context }) => {
const { locale } = context
const { slug, category } = params
return fetchBlogPost({ slug, category, locale })
},
component: BlogPostComponent,
})
function BlogPostComponent() {
const params = Route.useParams()
const data = Route.useLoaderData()
return (
<article>
<h1>{data.title}</h1>
<p>
Category: {params().category || 'All'} | Language:{' '}
{params().locale || 'en'}
</p>
<div>{data.content}</div>
</article>
)
}
다음과 같은 URL을 지원합니다.
/blog/tech/my-post(기본 로케일, tech 카테고리)/fr/blog/my-post(프랑스어, 카테고리 없음)/en/blog/tech/my-post(명시적 영어, tech 카테고리)/es/blog/tecnologia/mi-post(스페인어, 스페인어 카테고리)
언어 탐색
함수 방식의 매개변수와 선택적 i18n 매개변수를 사용해 언어 전환기를 만듭니다.
React
function LanguageSwitcher() {
const currentParams = useParams({ strict: false })
const languages = [
{ code: 'en', name: 'English' },
{ code: 'fr', name: 'Français' },
{ code: 'es', name: 'Español' },
]
return (
<div className="language-switcher">
{languages.map(({ code, name }) => (
<Link
key={code}
to="/{-$locale}/blog/{-$category}/$slug"
params={(prev) => ({
...prev,
locale: code === 'en' ? undefined : code, // Remove 'en' for clean URLs
})}
className={currentParams.locale === code ? 'active' : ''}
>
{name}
</Link>
))}
</div>
)
}
Solid
function LanguageSwitcher() {
const currentParams = useParams({ strict: false })
const languages = [
{ code: 'en', name: 'English' },
{ code: 'fr', name: 'Français' },
{ code: 'es', name: 'Español' },
]
return (
<div class="language-switcher">
{languages.map(({ code, name }) => (
<Link
to="/{-$locale}/blog/{-$category}/$slug"
params={(prev) => ({
...prev,
locale: code === 'en' ? undefined : code, // Remove 'en' for clean URLs
})}
class={currentParams().locale === code ? 'active' : ''}
>
{name}
</Link>
))}
</div>
)
}
더 정교한 언어 전환 로직을 만들 수도 있습니다.
React
function AdvancedLanguageSwitcher() {
const currentParams = useParams({ strict: false })
const handleLanguageChange = (newLocale: string) => {
return (prev: any) => {
// Preserve all existing params but update locale
const updatedParams = { ...prev }
if (newLocale === 'en') {
// Remove locale for clean English URLs
delete updatedParams.locale
} else {
updatedParams.locale = newLocale
}
return updatedParams
}
}
return (
<div className="language-switcher">
<Link
to="/{-$locale}/blog/{-$category}/$slug"
params={handleLanguageChange('fr')}
>
Français
</Link>
<Link
to="/{-$locale}/blog/{-$category}/$slug"
params={handleLanguageChange('es')}
>
Español
</Link>
<Link
to="/{-$locale}/blog/{-$category}/$slug"
params={handleLanguageChange('en')}
>
English
</Link>
</div>
)
}
Solid
function AdvancedLanguageSwitcher() {
const currentParams = useParams({ strict: false })
const handleLanguageChange = (newLocale: string) => {
return (prev: any) => {
// Preserve all existing params but update locale
const updatedParams = { ...prev }
if (newLocale === 'en') {
// Remove locale for clean English URLs
delete updatedParams.locale
} else {
updatedParams.locale = newLocale
}
return updatedParams
}
}
return (
<div class="language-switcher">
<Link
to="/{-$locale}/blog/{-$category}/$slug"
params={handleLanguageChange('fr')}
>
Français
</Link>
<Link
to="/{-$locale}/blog/{-$category}/$slug"
params={handleLanguageChange('es')}
>
Español
</Link>
<Link
to="/{-$locale}/blog/{-$category}/$slug"
params={handleLanguageChange('en')}
>
English
</Link>
</div>
)
}
선택적 매개변수를 사용하는 고급 i18n
유연한 로케일 처리를 위해 선택적 매개변수로 i18n 라우트를 구성합니다.
React
// Route structure:
// routes/
// {-$locale}/
// index.tsx // /, /en, /fr
// about.tsx // /about, /en/about, /fr/about
// blog/
// index.tsx // /blog, /en/blog, /fr/blog
// $slug.tsx // /blog/post, /en/blog/post, /fr/blog/post
// routes/{-$locale}/index.tsx
export const Route = createFileRoute('/{-$locale}/')({
component: HomeComponent,
})
function HomeComponent() {
const { locale } = Route.useParams()
const isRTL = ['ar', 'he', 'fa'].includes(locale || '')
return (
<div dir={isRTL ? 'rtl' : 'ltr'}>
<h1>Welcome ({locale || 'en'})</h1>
{/* Localized content */}
</div>
)
}
// routes/{-$locale}/about.tsx
export const Route = createFileRoute('/{-$locale}/about')({
component: AboutComponent,
})
Solid
// Route structure:
// routes/
// {-$locale}/
// index.tsx // /, /en, /fr
// about.tsx // /about, /en/about, /fr/about
// blog/
// index.tsx // /blog, /en/blog, /fr/blog
// $slug.tsx // /blog/post, /en/blog/post, /fr/blog/post
// routes/{-$locale}/index.tsx
export const Route = createFileRoute('/{-$locale}/')({
component: HomeComponent,
})
function HomeComponent() {
const params = Route.useParams()
const isRTL = ['ar', 'he', 'fa'].includes(params().locale || '')
return (
<div dir={isRTL ? 'rtl' : 'ltr'}>
<h1>Welcome ({params().locale || 'en'})</h1>
{/* Localized content */}
</div>
)
}
// routes/{-$locale}/about.tsx
export const Route = createFileRoute('/{-$locale}/about')({
component: AboutComponent,
})
SEO 및 표준 URL
i18n 라우트의 SEO를 올바르게 처리합니다.
React
export const Route = createFileRoute('/{-$locale}/products/$id')({
component: ProductComponent,
head: ({ params, loaderData }) => {
const locale = params.locale || 'en'
const product = loaderData
return {
title: product.title[locale] || product.title.en,
meta: [
{
name: 'description',
content: product.description[locale] || product.description.en,
},
{
property: 'og:locale',
content: locale,
},
],
links: [
// Canonical URL (always use default locale format)
{
rel: 'canonical',
href: `https://example.com/products/${params.id}`,
},
// Alternate language versions
{
rel: 'alternate',
hreflang: 'en',
href: `https://example.com/products/${params.id}`,
},
{
rel: 'alternate',
hreflang: 'fr',
href: `https://example.com/fr/products/${params.id}`,
},
{
rel: 'alternate',
hreflang: 'es',
href: `https://example.com/es/products/${params.id}`,
},
],
}
},
})
Solid
export const Route = createFileRoute('/{-$locale}/products/$id')({
component: ProductComponent,
head: ({ params, loaderData }) => {
const locale = params.locale || 'en'
const product = loaderData
return {
title: product.title[locale] || product.title.en,
meta: [
{
name: 'description',
content: product.description[locale] || product.description.en,
},
{
property: 'og:locale',
content: locale,
},
],
links: [
// Canonical URL (always use default locale format)
{
rel: 'canonical',
href: `https://example.com/products/${params.id}`,
},
// Alternate language versions
{
rel: 'alternate',
hreflang: 'en',
href: `https://example.com/products/${params.id}`,
},
{
rel: 'alternate',
hreflang: 'fr',
href: `https://example.com/fr/products/${params.id}`,
},
{
rel: 'alternate',
hreflang: 'es',
href: `https://example.com/es/products/${params.id}`,
},
],
}
},
})
i18n의 타입 안전성
i18n 구현의 타입 안전성을 확보합니다.
React
// Define supported locales
type Locale = 'en' | 'fr' | 'es' | 'de'
// Type-safe locale validation
function validateLocale(locale: string | undefined): locale is Locale {
return ['en', 'fr', 'es', 'de'].includes(locale as Locale)
}
export const Route = createFileRoute('/{-$locale}/shop/{-$category}')({
beforeLoad: async ({ params }) => {
const { locale } = params
// Type-safe locale validation
if (locale && !validateLocale(locale)) {
throw redirect({
to: '/shop/{-$category}',
params: { category: params.category },
})
}
return {
locale: (locale as Locale) || 'en',
isDefaultLocale: !locale || locale === 'en',
}
},
component: ShopComponent,
})
function ShopComponent() {
const { locale, category } = Route.useParams()
const { isDefaultLocale } = Route.useRouteContext()
// TypeScript knows locale is Locale | undefined
// and we have validated it in beforeLoad
return (
<div>
<h1>Shop {category ? `- ${category}` : ''}</h1>
<p>Language: {locale || 'en'}</p>
{!isDefaultLocale && (
<Link to="/shop/{-$category}" params={{ category }}>
View in English
</Link>
)}
</div>
)
}
Solid
// Define supported locales
type Locale = 'en' | 'fr' | 'es' | 'de'
// Type-safe locale validation
function validateLocale(locale: string | undefined): locale is Locale {
return ['en', 'fr', 'es', 'de'].includes(locale as Locale)
}
export const Route = createFileRoute('/{-$locale}/shop/{-$category}')({
beforeLoad: async ({ params }) => {
const { locale } = params
// Type-safe locale validation
if (locale && !validateLocale(locale)) {
throw redirect({
to: '/shop/{-$category}',
params: { category: params.category },
})
}
return {
locale: (locale as Locale) || 'en',
isDefaultLocale: !locale || locale === 'en',
}
},
component: ShopComponent,
})
function ShopComponent() {
const params = Route.useParams()
const routeContext = Route.useRouteContext()
// TypeScript knows locale is Locale | undefined
// and we have validated it in beforeLoad
return (
<div>
<h1>Shop {params().category ? `- ${params().category}` : ''}</h1>
<p>Language: {params().locale || 'en'}</p>
{!routeContext().isDefaultLocale && (
<Link to="/shop/{-$category}" params={{ category: params().category }}>
View in English
</Link>
)}
</div>
)
}
선택적 경로 매개변수는 TanStack Router 애플리케이션에서 국제화를 구현하기 위한 강력하고 유연한 기반을 제공합니다. 접두사 기반 접근 방식이나 결합 방식을 사용하더라도 뛰어난 개발자 경험과 타입 안전성을 유지하면서 깔끔하고 SEO 친화적인 URL을 만들 수 있습니다.
허용되는 문자
기본적으로 경로 매개변수는 encodeURIComponent로 이스케이프됩니다. 다른 유효한 URI 문자(예: @ 또는 +)를 허용하려면 RouterOptions에서 지정할 수 있습니다.
Example usage:
const router = createRouter({
// ...
pathParamsAllowedCharacters: ['@'],
})
다음은 허용되는 문자의 목록입니다.
;:@&=+$,