docs-i18n 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/dist/server/server.js +20 -20
- package/package.json +1 -1
- package/template/app/utils/content-loader.ts +4 -0
- package/template/app/utils/docs.server.ts +7 -0
- package/template/content/blog/en/announcing-query-v5.md +110 -0
- package/template/content/blog/en/hello-world.md +26 -0
- package/template/content/blog/en/i18n-best-practices.md +57 -0
- package/template/content/blog/en/react-query-vs-swr.md +100 -0
- package/template/content/blog/en/state-management-2024.md +143 -0
- package/template/content/blog/en/tanstack-router-1.0.md +121 -0
- package/template/content/blog/ja/announcing-query-v5.md +110 -0
- package/template/content/blog/ja/hello-world.md +26 -0
- package/template/content/blog/zh-hans/announcing-query-v5.md +93 -0
- package/template/content/blog/zh-hans/hello-world.md +26 -0
- package/template/content/docs-i18n/docs.config.json +25 -0
- package/template/content/docs-i18n/en/architecture.md +222 -0
- package/template/content/docs-i18n/en/configuration.md +331 -0
- package/template/content/docs-i18n/en/deployment.md +209 -0
- package/template/content/docs-i18n/en/getting-started.md +168 -0
- package/template/content/docs.config.json +25 -0
- package/template/content/en/admin.md +151 -0
- package/template/content/en/architecture.md +222 -0
- package/template/content/en/cli.md +269 -0
- package/template/content/en/configuration.md +331 -0
- package/template/content/en/deployment.md +209 -0
- package/template/content/en/getting-started.md +168 -0
- package/template/content/form/docs.config.json +18 -0
- package/template/content/form/en/guides/validation.md +175 -0
- package/template/content/form/en/installation.md +63 -0
- package/template/content/form/en/overview.md +71 -0
- package/template/content/form/en/quick-start.md +121 -0
- package/template/content/form/ja/installation.md +63 -0
- package/template/content/form/ja/overview.md +71 -0
- package/template/content/form/zh-hans/installation.md +63 -0
- package/template/content/form/zh-hans/overview.md +71 -0
- package/template/content/query/docs.config.json +32 -0
- package/template/content/query/en/guides/mutations.md +126 -0
- package/template/content/query/en/guides/pagination.md +98 -0
- package/template/content/query/en/guides/queries.md +120 -0
- package/template/content/query/en/installation.md +78 -0
- package/template/content/query/en/overview.md +72 -0
- package/template/content/query/en/quick-start.md +108 -0
- package/template/content/query/ja/installation.md +78 -0
- package/template/content/query/ja/overview.md +72 -0
- package/template/content/query/zh-hans/guides/mutations.md +126 -0
- package/template/content/query/zh-hans/guides/pagination.md +98 -0
- package/template/content/query/zh-hans/guides/queries.md +120 -0
- package/template/content/query/zh-hans/installation.md +95 -0
- package/template/content/query/zh-hans/overview.md +72 -0
- package/template/content/query/zh-hans/quick-start.md +108 -0
- package/template/content/router/docs.config.json +18 -0
- package/template/content/router/en/guides/routing-concepts.md +131 -0
- package/template/content/router/en/installation.md +57 -0
- package/template/content/router/en/overview.md +74 -0
- package/template/content/router/en/quick-start.md +88 -0
- package/template/content/router/ja/installation.md +57 -0
- package/template/content/router/ja/overview.md +78 -0
- package/template/content/router/zh-hans/guides/routing-concepts.md +131 -0
- package/template/content/router/zh-hans/installation.md +57 -0
- package/template/content/router/zh-hans/overview.md +81 -0
- package/template/content/router/zh-hans/quick-start.md +88 -0
- package/template/content/table/docs.config.json +18 -0
- package/template/content/table/en/guides/column-definitions.md +135 -0
- package/template/content/table/en/installation.md +56 -0
- package/template/content/table/en/overview.md +79 -0
- package/template/content/table/en/quick-start.md +112 -0
- package/template/content/table/ja/installation.md +56 -0
- package/template/content/table/ja/overview.md +79 -0
- package/template/content/table/zh-hans/installation.md +56 -0
- package/template/content/table/zh-hans/overview.md +79 -0
- package/template/content/virtual/docs.config.json +18 -0
- package/template/content/virtual/en/guides/dynamic-sizing.md +129 -0
- package/template/content/virtual/en/installation.md +57 -0
- package/template/content/virtual/en/overview.md +74 -0
- package/template/content/virtual/en/quick-start.md +114 -0
- package/template/content/virtual/ja/installation.md +57 -0
- package/template/content/virtual/ja/overview.md +74 -0
- package/template/content/virtual/zh-hans/installation.md +57 -0
- package/template/content/virtual/zh-hans/overview.md +74 -0
|
@@ -15556,6 +15556,26 @@ async function getStartManifest(matchedRoutes) {
|
|
|
15556
15556
|
//#endregion
|
|
15557
15557
|
//#region \0#tanstack-start-server-fn-resolver
|
|
15558
15558
|
var manifest = {
|
|
15559
|
+
"a3d81974aeece150d4b02be5b91590b8187442ebea56be4a89dcbf053626d22b": {
|
|
15560
|
+
functionName: "fetchVersion_createServerFn_handler",
|
|
15561
|
+
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15562
|
+
},
|
|
15563
|
+
"3bf4ba50ca8ccc3c8c60d8f2e53307a320940d68c478df494552066904c5cd74": {
|
|
15564
|
+
functionName: "fetchLlmConfig_createServerFn_handler",
|
|
15565
|
+
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15566
|
+
},
|
|
15567
|
+
"e0b4116f6b2c8d096830102e36458acf9c616a056fcdddda956a4d66984ef58c": {
|
|
15568
|
+
functionName: "fetchConfig_createServerFn_handler",
|
|
15569
|
+
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15570
|
+
},
|
|
15571
|
+
"a054a04356fe9987891efee8b7a11cd2dedb00f6b2e8f26d1c642e001e553d53": {
|
|
15572
|
+
functionName: "openFile_createServerFn_handler",
|
|
15573
|
+
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15574
|
+
},
|
|
15575
|
+
"5080dc3f2f2309ec6981b94c431969637130c657e8a1dfb10400b4614eecc1ea": {
|
|
15576
|
+
functionName: "fetchModels_createServerFn_handler",
|
|
15577
|
+
importer: () => import("./assets/models-YNa3F3nn.js")
|
|
15578
|
+
},
|
|
15559
15579
|
"4e218d79545765572808c7eab33b7663d4496209c15406d0b449366905b6b83f": {
|
|
15560
15580
|
functionName: "fetchStatus_createServerFn_handler",
|
|
15561
15581
|
importer: () => import("./assets/status-CM7Azp4n.js")
|
|
@@ -15576,10 +15596,6 @@ var manifest = {
|
|
|
15576
15596
|
functionName: "rescanVersion_createServerFn_handler",
|
|
15577
15597
|
importer: () => import("./assets/status-CM7Azp4n.js")
|
|
15578
15598
|
},
|
|
15579
|
-
"5080dc3f2f2309ec6981b94c431969637130c657e8a1dfb10400b4614eecc1ea": {
|
|
15580
|
-
functionName: "fetchModels_createServerFn_handler",
|
|
15581
|
-
importer: () => import("./assets/models-YNa3F3nn.js")
|
|
15582
|
-
},
|
|
15583
15599
|
"421de02ce39dde6e27cf4689e837ec072cbd01e63f8cdd5c2a3f42f0bd5ca613": {
|
|
15584
15600
|
functionName: "fetchJobs_createServerFn_handler",
|
|
15585
15601
|
importer: () => import("./assets/jobs-FXffC7LH.js")
|
|
@@ -15595,22 +15611,6 @@ var manifest = {
|
|
|
15595
15611
|
"88c2855c84e91504070bfecc50ddfa50339d22c305626800b6d9b05d79385d71": {
|
|
15596
15612
|
functionName: "deleteJob_createServerFn_handler",
|
|
15597
15613
|
importer: () => import("./assets/jobs-FXffC7LH.js")
|
|
15598
|
-
},
|
|
15599
|
-
"a3d81974aeece150d4b02be5b91590b8187442ebea56be4a89dcbf053626d22b": {
|
|
15600
|
-
functionName: "fetchVersion_createServerFn_handler",
|
|
15601
|
-
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15602
|
-
},
|
|
15603
|
-
"3bf4ba50ca8ccc3c8c60d8f2e53307a320940d68c478df494552066904c5cd74": {
|
|
15604
|
-
functionName: "fetchLlmConfig_createServerFn_handler",
|
|
15605
|
-
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15606
|
-
},
|
|
15607
|
-
"e0b4116f6b2c8d096830102e36458acf9c616a056fcdddda956a4d66984ef58c": {
|
|
15608
|
-
functionName: "fetchConfig_createServerFn_handler",
|
|
15609
|
-
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15610
|
-
},
|
|
15611
|
-
"a054a04356fe9987891efee8b7a11cd2dedb00f6b2e8f26d1c642e001e553d53": {
|
|
15612
|
-
functionName: "openFile_createServerFn_handler",
|
|
15613
|
-
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15614
15614
|
}
|
|
15615
15615
|
};
|
|
15616
15616
|
async function getServerFnById(id) {
|
package/package.json
CHANGED
|
@@ -82,7 +82,11 @@ export function createFsLoader(
|
|
|
82
82
|
const baseDirs = [
|
|
83
83
|
resolve(projectRoot, 'content', project, version, lang),
|
|
84
84
|
resolve(projectRoot, 'content', project, lang),
|
|
85
|
+
resolve(projectRoot, 'content', version, lang),
|
|
85
86
|
resolve(projectRoot, 'content', lang),
|
|
87
|
+
// Flat structure (no lang subdir): content/{version}/ directly
|
|
88
|
+
resolve(projectRoot, 'content', project, version),
|
|
89
|
+
resolve(projectRoot, 'content', version),
|
|
86
90
|
]
|
|
87
91
|
|
|
88
92
|
if (!urlMapper) {
|
|
@@ -108,7 +108,11 @@ function loadFilesystemSidebar(
|
|
|
108
108
|
const candidates = [
|
|
109
109
|
resolve(root, 'content', project, version, 'en'),
|
|
110
110
|
resolve(root, 'content', project, 'en'),
|
|
111
|
+
resolve(root, 'content', version, 'en'),
|
|
111
112
|
resolve(root, 'content', 'en'),
|
|
113
|
+
// Flat structure (no lang subdir)
|
|
114
|
+
resolve(root, 'content', project, version),
|
|
115
|
+
resolve(root, 'content', version),
|
|
112
116
|
]
|
|
113
117
|
for (const dir of candidates) {
|
|
114
118
|
if (existsSync(dir) && statSync(dir).isDirectory()) {
|
|
@@ -130,7 +134,10 @@ function autoScanDocs(
|
|
|
130
134
|
const candidates = [
|
|
131
135
|
resolve(root, 'content', project, version, 'en'),
|
|
132
136
|
resolve(root, 'content', project, 'en'),
|
|
137
|
+
resolve(root, 'content', version, 'en'),
|
|
133
138
|
resolve(root, 'content', 'en'),
|
|
139
|
+
resolve(root, 'content', project, version),
|
|
140
|
+
resolve(root, 'content', version),
|
|
134
141
|
]
|
|
135
142
|
|
|
136
143
|
for (const dir of candidates) {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Announcing TanStack Query v5"
|
|
3
|
+
published: "2024-10-01"
|
|
4
|
+
excerpt: "TanStack Query v5 is here with a simplified API, better TypeScript support, improved devtools, and a smaller bundle size. Here is everything you need to know."
|
|
5
|
+
authors:
|
|
6
|
+
- "Tanner Linsley"
|
|
7
|
+
- "Dominik Dorfmeister"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
We are thrilled to announce the release of TanStack Query v5! This release represents months of work from the community and brings significant improvements across the board.
|
|
11
|
+
|
|
12
|
+
## What's New
|
|
13
|
+
|
|
14
|
+
### Simplified API
|
|
15
|
+
|
|
16
|
+
The most visible change in v5 is the simplified API. We have removed the object syntax overloads in favor of a single, consistent object syntax for all hooks:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
// v4 — multiple overloads
|
|
20
|
+
useQuery(['todos'], fetchTodos)
|
|
21
|
+
useQuery(['todos'], fetchTodos, { staleTime: 5000 })
|
|
22
|
+
useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
|
|
23
|
+
|
|
24
|
+
// v5 — single consistent API
|
|
25
|
+
useQuery({
|
|
26
|
+
queryKey: ['todos'],
|
|
27
|
+
queryFn: fetchTodos,
|
|
28
|
+
staleTime: 5000,
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This makes the API easier to learn and provides better TypeScript inference.
|
|
33
|
+
|
|
34
|
+
### Better TypeScript Support
|
|
35
|
+
|
|
36
|
+
v5 includes major TypeScript improvements:
|
|
37
|
+
|
|
38
|
+
- **Strict query key typing** — query keys are now strictly typed throughout
|
|
39
|
+
- **Inferred return types** — `useQuery` correctly infers the return type from `queryFn`
|
|
40
|
+
- **Type-safe error handling** — errors are properly typed with the new `Error` generic parameter
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
// Error type is now inferred and type-safe
|
|
44
|
+
const { data, error } = useQuery({
|
|
45
|
+
queryKey: ['user', userId],
|
|
46
|
+
queryFn: async () => {
|
|
47
|
+
const res = await fetch(`/api/users/${userId}`)
|
|
48
|
+
if (!res.ok) throw new ApiError(res.status, await res.text())
|
|
49
|
+
return res.json() as Promise<User>
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// error is typed as ApiError | null
|
|
54
|
+
if (error) {
|
|
55
|
+
console.log(error.status) // TypeScript knows this exists
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### New Devtools
|
|
60
|
+
|
|
61
|
+
The devtools have been completely rewritten with a new design, better performance, and new features:
|
|
62
|
+
|
|
63
|
+
- **Query timeline** — visualize query fetches over time
|
|
64
|
+
- **Mutation inspector** — inspect mutation state and variables
|
|
65
|
+
- **Cache explorer** — browse the entire query cache
|
|
66
|
+
- **Online/offline toggle** — test offline behavior
|
|
67
|
+
|
|
68
|
+
### Smaller Bundle Size
|
|
69
|
+
|
|
70
|
+
We have reduced the core bundle size by approximately 20% through tree-shaking improvements and removing deprecated APIs.
|
|
71
|
+
|
|
72
|
+
| Package | v4 | v5 | Change |
|
|
73
|
+
|---|---|---|---|
|
|
74
|
+
| `@tanstack/react-query` | 12.4 KB | 9.8 KB | -21% |
|
|
75
|
+
| `@tanstack/query-core` | 10.1 KB | 8.2 KB | -19% |
|
|
76
|
+
|
|
77
|
+
## Breaking Changes
|
|
78
|
+
|
|
79
|
+
> [!WARNING]
|
|
80
|
+
> v5 includes several breaking changes. Please review the full [migration guide](/en/query/docs/guides/migration-v5) before upgrading.
|
|
81
|
+
|
|
82
|
+
Key breaking changes:
|
|
83
|
+
|
|
84
|
+
1. **Removed overloaded signatures** — all hooks now use object-only syntax
|
|
85
|
+
2. **Renamed `cacheTime` to `gcTime`** — better reflects its purpose (garbage collection time)
|
|
86
|
+
3. **`status: 'loading'` renamed to `status: 'pending'`** — aligns with Promise terminology
|
|
87
|
+
4. **Removed `keepPreviousData`** — replaced with `placeholderData: keepPreviousData` (import from query core)
|
|
88
|
+
5. **Minimum TypeScript version is now 4.7**
|
|
89
|
+
|
|
90
|
+
## Migration
|
|
91
|
+
|
|
92
|
+
We have built a codemod to automate most of the migration:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx @tanstack/query-codemod v5 ./src
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This will handle the most common transformations automatically. Review the changes and test thoroughly.
|
|
99
|
+
|
|
100
|
+
## Thank You
|
|
101
|
+
|
|
102
|
+
Thank you to the 100+ contributors who made this release possible. TanStack Query v5 would not exist without the incredible open source community.
|
|
103
|
+
|
|
104
|
+
Get started with v5 today:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm install @tanstack/react-query@latest
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Happy querying!
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Hello World: Introducing docs-i18n Blog"
|
|
3
|
+
published: "2024-01-15"
|
|
4
|
+
excerpt: "Welcome to the docs-i18n blog! We will share updates, tutorials, and best practices for internationalizing your documentation."
|
|
5
|
+
authors:
|
|
6
|
+
- "docs-i18n Team"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Welcome to the docs-i18n blog! This is where we will share project updates, tutorials, and best practices for documentation internationalization.
|
|
10
|
+
|
|
11
|
+
## What is docs-i18n?
|
|
12
|
+
|
|
13
|
+
docs-i18n is a universal documentation translation engine that makes it easy to maintain multilingual documentation sites. It provides:
|
|
14
|
+
|
|
15
|
+
- **Filesystem-based content loading** with i18n fallback
|
|
16
|
+
- **Markdown rendering** with syntax highlighting and framework-aware content
|
|
17
|
+
- **Admin interface** for managing translations
|
|
18
|
+
- **CLI tools** for automating translation workflows
|
|
19
|
+
|
|
20
|
+
## Getting Started
|
|
21
|
+
|
|
22
|
+
Check out our [getting started guide](/en/getting-started) to learn how to set up docs-i18n for your project.
|
|
23
|
+
|
|
24
|
+
## Stay Tuned
|
|
25
|
+
|
|
26
|
+
We have exciting features planned for upcoming releases. Follow this blog for the latest updates!
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Best Practices for Documentation i18n"
|
|
3
|
+
published: "2024-02-10"
|
|
4
|
+
excerpt: "Learn the key principles and patterns for effectively internationalizing your documentation, from content structure to translation workflows."
|
|
5
|
+
authors:
|
|
6
|
+
- "docs-i18n Team"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Internationalizing documentation is more than just translating text. Here are the best practices we have learned from building docs-i18n.
|
|
10
|
+
|
|
11
|
+
## Structure Content for Translation
|
|
12
|
+
|
|
13
|
+
### Use English as Source of Truth
|
|
14
|
+
|
|
15
|
+
Keep your English content as the canonical source. All translations derive from it, and the system falls back to English when a translation is missing.
|
|
16
|
+
|
|
17
|
+
### Separate Content from Presentation
|
|
18
|
+
|
|
19
|
+
Store your documentation as plain markdown files organized by locale:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
content/
|
|
23
|
+
blog/
|
|
24
|
+
en/
|
|
25
|
+
my-post.md
|
|
26
|
+
zh-hans/
|
|
27
|
+
my-post.md
|
|
28
|
+
ja/
|
|
29
|
+
my-post.md
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Use Frontmatter for Metadata
|
|
33
|
+
|
|
34
|
+
Each document should have frontmatter with at least a `title` field. For blog posts, include `published`, `excerpt`, and `authors`.
|
|
35
|
+
|
|
36
|
+
## Translation Workflow
|
|
37
|
+
|
|
38
|
+
### Start with High-Impact Pages
|
|
39
|
+
|
|
40
|
+
Not all pages need translation at once. Prioritize:
|
|
41
|
+
|
|
42
|
+
1. Getting started guides
|
|
43
|
+
2. Core concept documentation
|
|
44
|
+
3. API reference
|
|
45
|
+
4. Blog posts and announcements
|
|
46
|
+
|
|
47
|
+
### Leverage Fallback Content
|
|
48
|
+
|
|
49
|
+
docs-i18n automatically falls back to English when a translation is missing. This means your site works for all locales immediately — translations improve it incrementally.
|
|
50
|
+
|
|
51
|
+
### Keep Translations in Sync
|
|
52
|
+
|
|
53
|
+
When the English source changes, translations may become outdated. Use the docs-i18n admin interface to track which translations need updating.
|
|
54
|
+
|
|
55
|
+
## Conclusion
|
|
56
|
+
|
|
57
|
+
Good documentation internationalization is an ongoing process. Start simple, prioritize high-impact content, and iterate.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "TanStack Query vs SWR: A Detailed Comparison"
|
|
3
|
+
published: "2024-06-15"
|
|
4
|
+
excerpt: "An honest, detailed comparison of TanStack Query and SWR — two of the most popular data fetching libraries in the React ecosystem."
|
|
5
|
+
authors:
|
|
6
|
+
- "docs-i18n Team"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Choosing a data fetching library is an important architectural decision. TanStack Query and SWR are the two most popular options in the React ecosystem. Here is an honest comparison to help you decide.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Both libraries solve the same core problem: managing server state in React applications. They both provide hooks for fetching, caching, and synchronizing server data. However, they differ significantly in scope and philosophy.
|
|
14
|
+
|
|
15
|
+
| Feature | TanStack Query | SWR |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Bundle size (minified + gzip) | ~12 KB | ~4 KB |
|
|
18
|
+
| Framework support | React, Vue, Solid, Svelte, Angular | React only |
|
|
19
|
+
| Devtools | Built-in, feature-rich | Community extension |
|
|
20
|
+
| Mutations | First-class `useMutation` | Manual implementation |
|
|
21
|
+
| Infinite queries | Built-in `useInfiniteQuery` | Built-in `useSWRInfinite` |
|
|
22
|
+
| Offline support | Built-in | Manual |
|
|
23
|
+
| Query cancellation | Built-in with AbortSignal | Manual |
|
|
24
|
+
| Garbage collection | Configurable `gcTime` | No automatic GC |
|
|
25
|
+
| Optimistic updates | Built-in helpers | Manual |
|
|
26
|
+
|
|
27
|
+
## Data Fetching
|
|
28
|
+
|
|
29
|
+
Both libraries make basic data fetching simple:
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
// TanStack Query
|
|
33
|
+
import { useQuery } from '@tanstack/react-query'
|
|
34
|
+
|
|
35
|
+
function Todos() {
|
|
36
|
+
const { data, isLoading } = useQuery({
|
|
37
|
+
queryKey: ['todos'],
|
|
38
|
+
queryFn: () => fetch('/api/todos').then((r) => r.json()),
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// SWR
|
|
43
|
+
import useSWR from 'swr'
|
|
44
|
+
|
|
45
|
+
function Todos() {
|
|
46
|
+
const { data, isLoading } = useSWR('/api/todos', (url) =>
|
|
47
|
+
fetch(url).then((r) => r.json())
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
SWR has a slightly more concise API for simple cases. TanStack Query requires explicit query keys and uses an object syntax exclusively.
|
|
53
|
+
|
|
54
|
+
## Mutations
|
|
55
|
+
|
|
56
|
+
This is where the libraries diverge significantly. TanStack Query provides a dedicated `useMutation` hook:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
// TanStack Query — built-in mutation support
|
|
60
|
+
const mutation = useMutation({
|
|
61
|
+
mutationFn: (newTodo) => fetch('/api/todos', {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
body: JSON.stringify(newTodo),
|
|
64
|
+
}),
|
|
65
|
+
onSuccess: () => {
|
|
66
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] })
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Provides isPending, isError, isSuccess, mutate, mutateAsync
|
|
71
|
+
mutation.mutate({ title: 'New Todo' })
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
SWR does not have a built-in mutation hook. You typically use `useSWRMutation` from the `swr/mutation` module or handle mutations manually.
|
|
75
|
+
|
|
76
|
+
## When to Choose TanStack Query
|
|
77
|
+
|
|
78
|
+
Choose TanStack Query when:
|
|
79
|
+
|
|
80
|
+
- You need first-class mutation support with optimistic updates
|
|
81
|
+
- You want built-in devtools for debugging
|
|
82
|
+
- Your app has complex caching requirements
|
|
83
|
+
- You need offline support
|
|
84
|
+
- You might switch frameworks in the future (Vue, Solid, etc.)
|
|
85
|
+
|
|
86
|
+
## When to Choose SWR
|
|
87
|
+
|
|
88
|
+
Choose SWR when:
|
|
89
|
+
|
|
90
|
+
- Bundle size is critical
|
|
91
|
+
- You have simple data fetching needs without complex mutations
|
|
92
|
+
- You prefer a minimal API surface
|
|
93
|
+
- You are already in the Vercel/Next.js ecosystem
|
|
94
|
+
|
|
95
|
+
## Conclusion
|
|
96
|
+
|
|
97
|
+
Both are excellent libraries. TanStack Query is more feature-rich and better suited for complex applications with lots of mutations and caching needs. SWR is lighter and simpler, ideal for applications with straightforward data fetching requirements.
|
|
98
|
+
|
|
99
|
+
> [!NOTE]
|
|
100
|
+
> This comparison is based on TanStack Query v5 and SWR v2. Both libraries are actively maintained and improve with each release.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "The State of State Management in 2024"
|
|
3
|
+
published: "2024-08-20"
|
|
4
|
+
excerpt: "Server state vs client state, signals vs hooks, atoms vs stores — a practical guide to choosing the right state management approach for your React application in 2024."
|
|
5
|
+
authors:
|
|
6
|
+
- "docs-i18n Team"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
State management in React has evolved dramatically. The days of putting everything in Redux are over. In 2024, the ecosystem has matured into a set of specialized tools, each solving a specific problem well.
|
|
10
|
+
|
|
11
|
+
## The Two Kinds of State
|
|
12
|
+
|
|
13
|
+
The most important mental model shift in modern state management is the distinction between **server state** and **client state**.
|
|
14
|
+
|
|
15
|
+
**Server state** is data that:
|
|
16
|
+
- Lives on the server / database
|
|
17
|
+
- Is fetched asynchronously
|
|
18
|
+
- Can become stale
|
|
19
|
+
- Is shared across users
|
|
20
|
+
|
|
21
|
+
**Client state** is data that:
|
|
22
|
+
- Exists only in the browser
|
|
23
|
+
- Is synchronous
|
|
24
|
+
- Is local to the user's session
|
|
25
|
+
- Examples: UI toggles, form inputs, selected tabs
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// Server state — use TanStack Query
|
|
29
|
+
const { data: todos } = useQuery({
|
|
30
|
+
queryKey: ['todos'],
|
|
31
|
+
queryFn: fetchTodos,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Client state — use React state or a small store
|
|
35
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
36
|
+
const [selectedTab, setSelectedTab] = useState('all')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> [!NOTE]
|
|
40
|
+
> If you separate server state from client state, you will find that most applications need very little client state management. TanStack Query (or a similar library) handles the majority of your state.
|
|
41
|
+
|
|
42
|
+
## Server State Libraries
|
|
43
|
+
|
|
44
|
+
### TanStack Query
|
|
45
|
+
|
|
46
|
+
The most feature-rich option for server state management. Provides caching, background refetching, mutations with optimistic updates, pagination, infinite scroll, and excellent devtools.
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
50
|
+
|
|
51
|
+
function TodoApp() {
|
|
52
|
+
const queryClient = useQueryClient()
|
|
53
|
+
const todos = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
|
|
54
|
+
|
|
55
|
+
const addTodo = useMutation({
|
|
56
|
+
mutationFn: createTodo,
|
|
57
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div>
|
|
62
|
+
{todos.data?.map((todo) => <Todo key={todo.id} todo={todo} />)}
|
|
63
|
+
<button onClick={() => addTodo.mutate({ title: 'New' })}>Add</button>
|
|
64
|
+
</div>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### SWR
|
|
70
|
+
|
|
71
|
+
A lighter alternative focused on data fetching. Smaller bundle, simpler API, fewer features.
|
|
72
|
+
|
|
73
|
+
### tRPC
|
|
74
|
+
|
|
75
|
+
End-to-end type-safe APIs. Built on top of TanStack Query, so you get all its caching benefits plus type-safe API calls.
|
|
76
|
+
|
|
77
|
+
## Client State Libraries
|
|
78
|
+
|
|
79
|
+
### React `useState` / `useReducer`
|
|
80
|
+
|
|
81
|
+
For most client state, built-in React state is sufficient. Do not reach for a library until you have a clear need.
|
|
82
|
+
|
|
83
|
+
### Zustand
|
|
84
|
+
|
|
85
|
+
When you need global client state (theme, user preferences, complex UI state), Zustand provides a minimal, hook-based API:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { create } from 'zustand'
|
|
89
|
+
|
|
90
|
+
const useStore = create((set) => ({
|
|
91
|
+
bears: 0,
|
|
92
|
+
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
|
|
93
|
+
}))
|
|
94
|
+
|
|
95
|
+
function BearCounter() {
|
|
96
|
+
const bears = useStore((state) => state.bears)
|
|
97
|
+
return <h1>{bears} bears</h1>
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Jotai
|
|
102
|
+
|
|
103
|
+
Atomic state management inspired by Recoil. Great for fine-grained reactivity:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { atom, useAtom } from 'jotai'
|
|
107
|
+
|
|
108
|
+
const countAtom = atom(0)
|
|
109
|
+
const doubledAtom = atom((get) => get(countAtom) * 2)
|
|
110
|
+
|
|
111
|
+
function Counter() {
|
|
112
|
+
const [count, setCount] = useAtom(countAtom)
|
|
113
|
+
const [doubled] = useAtom(doubledAtom)
|
|
114
|
+
return <div>{count} (doubled: {doubled})</div>
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Decision Framework
|
|
119
|
+
|
|
120
|
+
| Need | Recommended Tool |
|
|
121
|
+
|---|---|
|
|
122
|
+
| Fetching / caching server data | TanStack Query or SWR |
|
|
123
|
+
| Local component state | `useState` / `useReducer` |
|
|
124
|
+
| Shared UI state (theme, modals) | Zustand or Jotai |
|
|
125
|
+
| Complex form state | TanStack Form |
|
|
126
|
+
| URL state (search params, pagination) | TanStack Router |
|
|
127
|
+
| End-to-end type-safe API | tRPC + TanStack Query |
|
|
128
|
+
|
|
129
|
+
## The Pattern
|
|
130
|
+
|
|
131
|
+
The most successful React applications in 2024 follow this pattern:
|
|
132
|
+
|
|
133
|
+
1. **TanStack Query** for all server state
|
|
134
|
+
2. **TanStack Router** for URL-driven state
|
|
135
|
+
3. **React `useState`** for local UI state
|
|
136
|
+
4. **Zustand or Jotai** (only if needed) for global client state
|
|
137
|
+
|
|
138
|
+
> [!WARNING]
|
|
139
|
+
> Avoid putting server data in a global client state store (Redux, Zustand, etc.). This leads to stale data, duplicated caching logic, and unnecessary complexity. Use TanStack Query for server state.
|
|
140
|
+
|
|
141
|
+
## Conclusion
|
|
142
|
+
|
|
143
|
+
State management in 2024 is not about choosing one library to rule them all. It is about using the right tool for the right kind of state. Start simple, add complexity only when needed, and let specialized libraries handle what they do best.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "TanStack Router 1.0: Type-Safe Routing for React"
|
|
3
|
+
published: "2024-03-15"
|
|
4
|
+
excerpt: "TanStack Router 1.0 is stable! With 100% type-safe navigation, built-in search params, and first-class data loading, it is the most type-safe router for React."
|
|
5
|
+
authors:
|
|
6
|
+
- "Tanner Linsley"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
After years of development and months of beta testing, we are proud to announce TanStack Router 1.0 is stable and production-ready.
|
|
10
|
+
|
|
11
|
+
## Why Another Router?
|
|
12
|
+
|
|
13
|
+
React Router has served the community well for years. But as TypeScript adoption has grown, developers have increasingly wanted a router that provides end-to-end type safety. TanStack Router was built from the ground up with TypeScript as a first-class citizen.
|
|
14
|
+
|
|
15
|
+
## Key Features
|
|
16
|
+
|
|
17
|
+
### 100% Type-Safe Navigation
|
|
18
|
+
|
|
19
|
+
Every `Link`, `navigate`, and `redirect` in TanStack Router is fully type-checked:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
// TypeScript will error if:
|
|
23
|
+
// - The route does not exist
|
|
24
|
+
// - Required params are missing
|
|
25
|
+
// - Search params do not match the schema
|
|
26
|
+
<Link
|
|
27
|
+
to="/users/$userId/posts/$postId"
|
|
28
|
+
params={{ userId: '1', postId: '42' }}
|
|
29
|
+
search={{ sort: 'date' }}
|
|
30
|
+
/>
|
|
31
|
+
|
|
32
|
+
// This would be a type error:
|
|
33
|
+
<Link
|
|
34
|
+
to="/users/$userId/posts/$postId"
|
|
35
|
+
params={{ userId: '1' }} // Error: missing postId
|
|
36
|
+
/>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### First-Class Search Params
|
|
40
|
+
|
|
41
|
+
URL search parameters are validated and typed:
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
const productsRoute = createRoute({
|
|
45
|
+
path: '/products',
|
|
46
|
+
validateSearch: z.object({
|
|
47
|
+
page: z.number().default(1),
|
|
48
|
+
category: z.string().optional(),
|
|
49
|
+
sort: z.enum(['price', 'name', 'rating']).default('name'),
|
|
50
|
+
}),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// In your component:
|
|
54
|
+
const { page, category, sort } = productsRoute.useSearch()
|
|
55
|
+
// ^ All fully typed!
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Built-In Data Loading
|
|
59
|
+
|
|
60
|
+
No need for separate data fetching libraries for route-level data:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
const userRoute = createRoute({
|
|
64
|
+
path: '/users/$userId',
|
|
65
|
+
loader: async ({ params, context }) => {
|
|
66
|
+
return context.queryClient.ensureQueryData({
|
|
67
|
+
queryKey: ['user', params.userId],
|
|
68
|
+
queryFn: () => fetchUser(params.userId),
|
|
69
|
+
})
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### File-Based Routing
|
|
75
|
+
|
|
76
|
+
For larger applications, file-based routing generates the type-safe route tree automatically:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
routes/
|
|
80
|
+
__root.tsx
|
|
81
|
+
index.tsx
|
|
82
|
+
about.tsx
|
|
83
|
+
users.tsx
|
|
84
|
+
users.$userId.tsx
|
|
85
|
+
users.$userId.posts.tsx
|
|
86
|
+
users.$userId.posts.$postId.tsx
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The router plugin generates a `routeTree.gen.ts` file with complete type information.
|
|
90
|
+
|
|
91
|
+
## Migration from React Router
|
|
92
|
+
|
|
93
|
+
We understand that migrating routers is a significant effort. We have published a detailed [migration guide](/en/router/docs/guides/migration-from-react-router) and built a codemod to help:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx @tanstack/router-codemod from-react-router ./src
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Performance
|
|
100
|
+
|
|
101
|
+
TanStack Router is designed for performance:
|
|
102
|
+
|
|
103
|
+
- **Route-level code splitting** works out of the box with file-based routing
|
|
104
|
+
- **Prefetching** on link hover ensures instant navigations
|
|
105
|
+
- **Parallel data loading** fetches data for all matched routes simultaneously
|
|
106
|
+
- **Stale-while-revalidate** keeps the UI responsive during refetches
|
|
107
|
+
|
|
108
|
+
> [!NOTE]
|
|
109
|
+
> TanStack Router pairs beautifully with TanStack Query. Use the router for route-level data loading and Query for component-level data fetching.
|
|
110
|
+
|
|
111
|
+
## Get Started
|
|
112
|
+
|
|
113
|
+
Install TanStack Router today:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm install @tanstack/react-router @tanstack/router-plugin
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Read the [quick start guide](/en/router/docs/quick-start) to build your first type-safe route.
|
|
120
|
+
|
|
121
|
+
Thank you to everyone who contributed during the alpha and beta phases. Your feedback shaped this release into something we are truly proud of.
|