docs-i18n 0.8.1 → 0.8.3
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 +32 -32
- package/package.json +1 -1
- package/template/app/routes/$lang.$project.$version.docs.$.tsx +2 -1
- package/template/app/routes/$lang.$project.$version.docs.framework.$framework.$.tsx +2 -0
- package/template/app/routes/$lang.$project.$version.docs.tsx +2 -1
- package/template/app/routes/$lang.$project.docs.$.tsx +2 -1
- package/template/app/routes/$lang.$project.docs.tsx +2 -1
- package/template/app/routes/$lang.docs.$.tsx +2 -1
- package/template/app/routes/$lang.docs.framework.$framework.$.tsx +2 -0
- package/template/app/routes/$lang.docs.tsx +2 -1
- package/template/app/utils/content-loader.ts +13 -2
- package/template/app/utils/docs.server.ts +17 -15
- 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 +335 -0
- package/template/content/docs-i18n/en/cli.md +13 -1
- package/template/content/docs-i18n/en/configuration.md +350 -0
- package/template/content/docs-i18n/en/deployment.md +222 -0
- package/template/content/docs-i18n/en/getting-started.md +189 -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,29 +15556,21 @@ async function getStartManifest(matchedRoutes) {
|
|
|
15556
15556
|
//#endregion
|
|
15557
15557
|
//#region \0#tanstack-start-server-fn-resolver
|
|
15558
15558
|
var manifest = {
|
|
15559
|
-
"
|
|
15560
|
-
functionName: "
|
|
15561
|
-
importer: () => import("./assets/
|
|
15562
|
-
},
|
|
15563
|
-
"843cd8b59095708a5ae78198708db9850f89fd3dd3830ab236f0bd924417692f": {
|
|
15564
|
-
functionName: "fetchFileCoverage_createServerFn_handler",
|
|
15565
|
-
importer: () => import("./assets/status-CM7Azp4n.js")
|
|
15566
|
-
},
|
|
15567
|
-
"e1e7281e45375c67dbe408c58452e7482b139da60e9e361615553227dce95ee0": {
|
|
15568
|
-
functionName: "fetchFileBlocks_createServerFn_handler",
|
|
15569
|
-
importer: () => import("./assets/status-CM7Azp4n.js")
|
|
15559
|
+
"a3d81974aeece150d4b02be5b91590b8187442ebea56be4a89dcbf053626d22b": {
|
|
15560
|
+
functionName: "fetchVersion_createServerFn_handler",
|
|
15561
|
+
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15570
15562
|
},
|
|
15571
|
-
"
|
|
15572
|
-
functionName: "
|
|
15573
|
-
importer: () => import("./assets/
|
|
15563
|
+
"3bf4ba50ca8ccc3c8c60d8f2e53307a320940d68c478df494552066904c5cd74": {
|
|
15564
|
+
functionName: "fetchLlmConfig_createServerFn_handler",
|
|
15565
|
+
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15574
15566
|
},
|
|
15575
|
-
"
|
|
15576
|
-
functionName: "
|
|
15577
|
-
importer: () => import("./assets/
|
|
15567
|
+
"e0b4116f6b2c8d096830102e36458acf9c616a056fcdddda956a4d66984ef58c": {
|
|
15568
|
+
functionName: "fetchConfig_createServerFn_handler",
|
|
15569
|
+
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15578
15570
|
},
|
|
15579
|
-
"
|
|
15580
|
-
functionName: "
|
|
15581
|
-
importer: () => import("./assets/
|
|
15571
|
+
"a054a04356fe9987891efee8b7a11cd2dedb00f6b2e8f26d1c642e001e553d53": {
|
|
15572
|
+
functionName: "openFile_createServerFn_handler",
|
|
15573
|
+
importer: () => import("./assets/misc-y6t3-UOP.js")
|
|
15582
15574
|
},
|
|
15583
15575
|
"421de02ce39dde6e27cf4689e837ec072cbd01e63f8cdd5c2a3f42f0bd5ca613": {
|
|
15584
15576
|
functionName: "fetchJobs_createServerFn_handler",
|
|
@@ -15596,21 +15588,29 @@ var manifest = {
|
|
|
15596
15588
|
functionName: "deleteJob_createServerFn_handler",
|
|
15597
15589
|
importer: () => import("./assets/jobs-FXffC7LH.js")
|
|
15598
15590
|
},
|
|
15599
|
-
"
|
|
15600
|
-
functionName: "
|
|
15601
|
-
importer: () => import("./assets/
|
|
15591
|
+
"4e218d79545765572808c7eab33b7663d4496209c15406d0b449366905b6b83f": {
|
|
15592
|
+
functionName: "fetchStatus_createServerFn_handler",
|
|
15593
|
+
importer: () => import("./assets/status-CM7Azp4n.js")
|
|
15602
15594
|
},
|
|
15603
|
-
"
|
|
15604
|
-
functionName: "
|
|
15605
|
-
importer: () => import("./assets/
|
|
15595
|
+
"843cd8b59095708a5ae78198708db9850f89fd3dd3830ab236f0bd924417692f": {
|
|
15596
|
+
functionName: "fetchFileCoverage_createServerFn_handler",
|
|
15597
|
+
importer: () => import("./assets/status-CM7Azp4n.js")
|
|
15606
15598
|
},
|
|
15607
|
-
"
|
|
15608
|
-
functionName: "
|
|
15609
|
-
importer: () => import("./assets/
|
|
15599
|
+
"e1e7281e45375c67dbe408c58452e7482b139da60e9e361615553227dce95ee0": {
|
|
15600
|
+
functionName: "fetchFileBlocks_createServerFn_handler",
|
|
15601
|
+
importer: () => import("./assets/status-CM7Azp4n.js")
|
|
15610
15602
|
},
|
|
15611
|
-
"
|
|
15612
|
-
functionName: "
|
|
15613
|
-
importer: () => import("./assets/
|
|
15603
|
+
"e1d13d8602339a95f559345ccfc82e9f843dd375ce8e9f580637c241e7e44774": {
|
|
15604
|
+
functionName: "deleteCacheEntry_createServerFn_handler",
|
|
15605
|
+
importer: () => import("./assets/status-CM7Azp4n.js")
|
|
15606
|
+
},
|
|
15607
|
+
"1d8e3916f992485c62e62c3693083850b773fdff4ec54277de5a01eb98dab664": {
|
|
15608
|
+
functionName: "rescanVersion_createServerFn_handler",
|
|
15609
|
+
importer: () => import("./assets/status-CM7Azp4n.js")
|
|
15610
|
+
},
|
|
15611
|
+
"5080dc3f2f2309ec6981b94c431969637130c657e8a1dfb10400b4614eecc1ea": {
|
|
15612
|
+
functionName: "fetchModels_createServerFn_handler",
|
|
15613
|
+
importer: () => import("./assets/models-YNa3F3nn.js")
|
|
15614
15614
|
}
|
|
15615
15615
|
};
|
|
15616
15616
|
async function getServerFnById(id) {
|
package/package.json
CHANGED
|
@@ -15,7 +15,8 @@ const fetchDoc = createServerFn({ method: 'GET' })
|
|
|
15
15
|
)
|
|
16
16
|
.handler(async ({ data }) => {
|
|
17
17
|
const { loadDoc } = await import('~/utils/docs.server')
|
|
18
|
-
|
|
18
|
+
const projectConfig = findProject(data.project)
|
|
19
|
+
return loadDoc(data.project, data.version, data.lang, data.slug, projectConfig)
|
|
19
20
|
})
|
|
20
21
|
|
|
21
22
|
export const Route = createFileRoute('/$lang/$project/$version/docs/$')({
|
|
@@ -19,11 +19,13 @@ const fetchFrameworkDoc = createServerFn({ method: 'GET' })
|
|
|
19
19
|
)
|
|
20
20
|
.handler(async ({ data }) => {
|
|
21
21
|
const { loadDoc } = await import('~/utils/docs.server')
|
|
22
|
+
const projectConfig = findProject(data.project)
|
|
22
23
|
return loadDoc(
|
|
23
24
|
data.project,
|
|
24
25
|
data.version,
|
|
25
26
|
data.lang,
|
|
26
27
|
`framework/${data.framework}/${data.slug}`,
|
|
28
|
+
projectConfig,
|
|
27
29
|
)
|
|
28
30
|
})
|
|
29
31
|
|
|
@@ -13,7 +13,8 @@ const fetchDocsConfig = createServerFn({ method: 'GET' })
|
|
|
13
13
|
.inputValidator((data: { project: string; version: string }) => data)
|
|
14
14
|
.handler(async ({ data }) => {
|
|
15
15
|
const { loadDocsConfig } = await import('~/utils/docs.server')
|
|
16
|
-
|
|
16
|
+
const projectConfig = findProject(data.project)
|
|
17
|
+
return loadDocsConfig(data.project, data.version, projectConfig)
|
|
17
18
|
})
|
|
18
19
|
|
|
19
20
|
export const Route = createFileRoute('/$lang/$project/$version/docs')({
|
|
@@ -13,7 +13,8 @@ const fetchDoc = createServerFn({ method: 'GET' })
|
|
|
13
13
|
)
|
|
14
14
|
.handler(async ({ data }) => {
|
|
15
15
|
const { loadDoc } = await import('~/utils/docs.server')
|
|
16
|
-
|
|
16
|
+
const projectConfig = findProject(data.project)
|
|
17
|
+
return loadDoc(data.project, data.version, data.lang, data.slug, projectConfig)
|
|
17
18
|
})
|
|
18
19
|
|
|
19
20
|
export const Route = createFileRoute('/$lang/$project/docs/$')({
|
|
@@ -13,7 +13,8 @@ const fetchDocsConfig = createServerFn({ method: 'GET' })
|
|
|
13
13
|
.inputValidator((data: { project: string; version: string }) => data)
|
|
14
14
|
.handler(async ({ data }) => {
|
|
15
15
|
const { loadDocsConfig } = await import('~/utils/docs.server')
|
|
16
|
-
|
|
16
|
+
const projectConfig = findProject(data.project)
|
|
17
|
+
return loadDocsConfig(data.project, data.version, projectConfig)
|
|
17
18
|
})
|
|
18
19
|
|
|
19
20
|
export const Route = createFileRoute('/$lang/$project/docs')({
|
|
@@ -13,7 +13,8 @@ const fetchDoc = createServerFn({ method: 'GET' })
|
|
|
13
13
|
)
|
|
14
14
|
.handler(async ({ data }) => {
|
|
15
15
|
const { loadDoc } = await import('~/utils/docs.server')
|
|
16
|
-
|
|
16
|
+
const projectConfig = getSingleProject()
|
|
17
|
+
return loadDoc(data.project, data.version, data.lang, data.slug, projectConfig)
|
|
17
18
|
})
|
|
18
19
|
|
|
19
20
|
export const Route = createFileRoute('/$lang/docs/$')({
|
|
@@ -19,11 +19,13 @@ const fetchFrameworkDoc = createServerFn({ method: 'GET' })
|
|
|
19
19
|
)
|
|
20
20
|
.handler(async ({ data }) => {
|
|
21
21
|
const { loadDoc } = await import('~/utils/docs.server')
|
|
22
|
+
const projectConfig = getSingleProject()
|
|
22
23
|
return loadDoc(
|
|
23
24
|
data.project,
|
|
24
25
|
data.version,
|
|
25
26
|
data.lang,
|
|
26
27
|
`framework/${data.framework}/${data.slug}`,
|
|
28
|
+
projectConfig,
|
|
27
29
|
)
|
|
28
30
|
})
|
|
29
31
|
|
|
@@ -15,7 +15,8 @@ const fetchDocsConfig = createServerFn({ method: 'GET' })
|
|
|
15
15
|
.inputValidator((data: { project: string; version: string }) => data)
|
|
16
16
|
.handler(async ({ data }) => {
|
|
17
17
|
const { loadDocsConfig } = await import('~/utils/docs.server')
|
|
18
|
-
|
|
18
|
+
const projectConfig = getSingleProject()
|
|
19
|
+
return loadDocsConfig(data.project, data.version, projectConfig)
|
|
19
20
|
})
|
|
20
21
|
|
|
21
22
|
export const Route = createFileRoute('/$lang/docs')({
|
|
@@ -64,6 +64,7 @@ function getTranslationCache(projectRoot: string): TranslationCache | null {
|
|
|
64
64
|
|
|
65
65
|
export function createFsLoader(
|
|
66
66
|
projectRoot: string,
|
|
67
|
+
docsRoot = 'content',
|
|
67
68
|
urlMapper?: (filePath: string) => string,
|
|
68
69
|
): ContentLoader {
|
|
69
70
|
/** Supported markdown extensions, in priority order. */
|
|
@@ -79,10 +80,20 @@ export function createFsLoader(
|
|
|
79
80
|
lang: string,
|
|
80
81
|
slug: string,
|
|
81
82
|
): { raw: string; filePath: string } | null {
|
|
83
|
+
// Resolve content base directory using docsRoot from site config.
|
|
84
|
+
// docsRoot encodes the project-specific path (e.g. 'content/query', 'content/docs').
|
|
85
|
+
// Convention: no /en/ subdir — English source files live directly under version/.
|
|
86
|
+
// Non-English comes from .cache/ (translation assembly).
|
|
82
87
|
const baseDirs = [
|
|
88
|
+
resolve(projectRoot, docsRoot, version),
|
|
89
|
+
resolve(projectRoot, docsRoot),
|
|
90
|
+
// Fallback: try content/{project}/{version} pattern
|
|
91
|
+
resolve(projectRoot, 'content', project, version),
|
|
92
|
+
resolve(projectRoot, 'content', version),
|
|
93
|
+
// Legacy: with lang subdir
|
|
83
94
|
resolve(projectRoot, 'content', project, version, lang),
|
|
84
|
-
resolve(projectRoot,
|
|
85
|
-
resolve(projectRoot,
|
|
95
|
+
resolve(projectRoot, docsRoot, version, lang),
|
|
96
|
+
resolve(projectRoot, docsRoot, lang),
|
|
86
97
|
]
|
|
87
98
|
|
|
88
99
|
if (!urlMapper) {
|
|
@@ -62,7 +62,7 @@ export function setContentLoader(loader: ContentLoader) {
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Load a document with i18n fallback.
|
|
65
|
-
*
|
|
65
|
+
* Uses docsRoot from projectConfig to resolve content paths.
|
|
66
66
|
*/
|
|
67
67
|
export async function loadDoc(
|
|
68
68
|
project: string,
|
|
@@ -71,11 +71,9 @@ export async function loadDoc(
|
|
|
71
71
|
slug: string,
|
|
72
72
|
projectConfig?: ProjectConfig,
|
|
73
73
|
): Promise<LoadedDoc> {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
return getLoader().loadDoc(project, version, lang, slug)
|
|
74
|
+
const docsRoot = projectConfig?.docsRoot || 'content'
|
|
75
|
+
const loader = createFsLoader(getProjectRoot(), docsRoot, projectConfig?.urlMapper)
|
|
76
|
+
return loader.loadDoc(project, version, lang, slug)
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
/**
|
|
@@ -91,11 +89,13 @@ export async function loadDocsConfig(
|
|
|
91
89
|
return loadFilesystemSidebar(project, version, projectConfig)
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
const
|
|
92
|
+
const docsRoot = projectConfig?.docsRoot || 'content'
|
|
93
|
+
const loader = createFsLoader(getProjectRoot(), docsRoot)
|
|
94
|
+
const config = await loader.loadDocsConfig(project, version)
|
|
95
95
|
if (config) return config
|
|
96
96
|
|
|
97
97
|
// Auto-scan fallback (filesystem only)
|
|
98
|
-
return autoScanDocs(getProjectRoot(), project, version)
|
|
98
|
+
return autoScanDocs(getProjectRoot(), docsRoot, project, version)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/** Generate sidebar from filesystem using numeric prefix ordering. */
|
|
@@ -105,10 +105,11 @@ function loadFilesystemSidebar(
|
|
|
105
105
|
projectConfig: ProjectConfig,
|
|
106
106
|
): DocsConfig {
|
|
107
107
|
const root = getProjectRoot()
|
|
108
|
+
const docsRoot = projectConfig.docsRoot || 'content'
|
|
108
109
|
const candidates = [
|
|
109
|
-
resolve(root,
|
|
110
|
-
resolve(root,
|
|
111
|
-
resolve(root, 'content',
|
|
110
|
+
resolve(root, docsRoot, version),
|
|
111
|
+
resolve(root, docsRoot),
|
|
112
|
+
resolve(root, 'content', project, version),
|
|
112
113
|
]
|
|
113
114
|
for (const dir of candidates) {
|
|
114
115
|
if (existsSync(dir) && statSync(dir).isDirectory()) {
|
|
@@ -120,17 +121,18 @@ function loadFilesystemSidebar(
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
/**
|
|
123
|
-
* Auto-generate sidebar config by scanning .md files in the content directory.
|
|
124
|
+
* Auto-generate sidebar config by scanning .md/.mdx files in the content directory.
|
|
124
125
|
*/
|
|
125
126
|
function autoScanDocs(
|
|
126
127
|
root: string,
|
|
128
|
+
docsRoot: string,
|
|
127
129
|
project: string,
|
|
128
130
|
version: string,
|
|
129
131
|
): DocsConfig {
|
|
130
132
|
const candidates = [
|
|
131
|
-
resolve(root,
|
|
132
|
-
resolve(root,
|
|
133
|
-
resolve(root, 'content',
|
|
133
|
+
resolve(root, docsRoot, version),
|
|
134
|
+
resolve(root, docsRoot),
|
|
135
|
+
resolve(root, 'content', project, version),
|
|
134
136
|
]
|
|
135
137
|
|
|
136
138
|
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.
|