@@ -57,24 +57,46 @@ const featureCards = [
5757 } ,
5858]
5959
60- const routeContracts = [
60+ const startRuntimeSteps = [
6161 {
62+ label : 'Match route' ,
6263 file : 'routes/_app.projects.$id.tsx' ,
63- tag : 'route' ,
64- title : 'Types flow from the route file.' ,
65- body : 'Path params, search schemas, route context, loader data, links, and navigate calls all stay tied to the generated route map.' ,
64+ body : 'Router narrows params, search, context, loader deps, and links before React starts rendering.' ,
6665 } ,
6766 {
68- file : 'loader: ({ context, params }) => ...' ,
69- tag : 'data' ,
70- title : 'Data starts before render.' ,
71- body : 'Loaders coordinate async work in parallel, preload on intent, use Router cache, or hydrate TanStack Query for client-side reuse.' ,
67+ label : 'Run loader' ,
68+ file : 'loader({ context, params })' ,
69+ body : 'Start can do the preload on the server, dehydrate Query, and reuse the same contract during client navigation.' ,
7270 } ,
7371 {
74- file : 'createServerFn({ method: "POST" })' ,
75- tag : 'server' ,
76- title : 'The server boundary is deliberate.' ,
77- body : 'Run database, environment, file-system, auth, or mutation work on the server while keeping the call typed and validated.' ,
72+ label : 'Call server function' ,
73+ file : 'createServerFn({ method: "GET" })' ,
74+ body : 'Database, auth, and environment work stay behind an explicit, validated server boundary.' ,
75+ } ,
76+ {
77+ label : 'Stream document' ,
78+ file : 'SSR + route data + pending UI' ,
79+ body : 'The HTML shell, head tags, loader data, and useful pending states can leave as one streaming response.' ,
80+ } ,
81+ {
82+ label : 'Ship runtime output' ,
83+ file : 'runtime adapter for the host' ,
84+ body : 'The same app model builds for the deployment target instead of changing how routes are authored.' ,
85+ } ,
86+ ]
87+
88+ const startRuntimeOutputs = [
89+ {
90+ label : 'Route contract' ,
91+ value : 'params.id, search.tab, loaderData' ,
92+ } ,
93+ {
94+ label : 'Server boundary' ,
95+ value : 'db, auth, env, files, mutations' ,
96+ } ,
97+ {
98+ label : 'Deploy output' ,
99+ value : 'Node, Workers, Netlify, Railway' ,
78100 } ,
79101]
80102
@@ -356,10 +378,6 @@ export default function StartLanding() {
356378}
357379
358380function StartRuntimePanel ( ) {
359- const [ activeContractIndex , setActiveContractIndex ] = React . useState ( 0 )
360- const activeContract =
361- routeContracts [ activeContractIndex ] ?? routeContracts [ 0 ]
362-
363381 return (
364382 < div className = "min-w-0 w-full max-w-full overflow-hidden rounded-lg border border-cyan-200 bg-white p-4 shadow-sm shadow-cyan-950/5 dark:border-cyan-900 dark:bg-zinc-950" >
365383 < div className = "flex items-center justify-between gap-3" >
@@ -375,92 +393,119 @@ function StartRuntimePanel() {
375393
376394 < div className = "mt-4" >
377395 < p className = "text-sm font-black leading-5 text-zinc-950 dark:text-white" >
378- Start begins where Router leaves off .
396+ Start keeps Router as the application contract .
379397 </ p >
380398 < p className = "mt-2 text-xs leading-5 text-zinc-600 dark:text-zinc-400" >
381- The route tree owns the application contract. Start adds execution,
382- server functions, server routes, and build output around it without
383- replacing that contract.
399+ Then it runs the server work Router shouldn't own: SSR, streaming,
400+ server functions, server routes, and deployable output.
384401 </ p >
385402 </ div >
386403
387- < div className = "mt-4 rounded-lg bg-cyan-50 p-3 dark:bg-cyan-950/30" >
388- < div className = "flex min-w-0 items-start justify-between gap-3" >
389- < p className = "min-w-0 truncate font-mono text-xs font-bold text-cyan-950 dark:text-cyan-100" >
390- { activeContract . file }
391- </ p >
392- < span className = "shrink-0 rounded-md bg-cyan-500 px-1.5 py-0.5 text-[0.65rem] font-black uppercase leading-none text-white" >
393- { activeContract . tag }
394- </ span >
404+ < div className = "mt-4 grid gap-4 xl:grid-cols-[1.04fr_0.96fr]" >
405+ < div className = "min-w-0 overflow-hidden rounded-lg bg-zinc-950 p-4 text-zinc-100 shadow-inner shadow-black/20 dark:bg-black" >
406+ < div className = "flex min-w-0 items-center justify-between gap-3" >
407+ < p className = "min-w-0 truncate font-mono text-xs font-bold text-cyan-200" >
408+ GET /projects/tanstack?tab=activity
409+ </ p >
410+ < span className = "shrink-0 rounded-md bg-emerald-400/15 px-1.5 py-0.5 text-[0.65rem] font-black uppercase leading-none text-emerald-200" >
411+ server first
412+ </ span >
413+ </ div >
414+
415+ < div className = "mt-4 min-w-0 overflow-x-auto pb-1 font-mono text-[0.72rem] leading-5" >
416+ < p className = "whitespace-nowrap" >
417+ < span className = "text-pink-300" > export const</ span > { ' ' }
418+ < span className = "text-white" > Route</ span > = createFileRoute(
419+ < span className = "text-emerald-300" > '/_app/projects/$id'</ span > )(
420+ { '{' }
421+ </ p >
422+ < p className = "whitespace-nowrap pl-4" >
423+ validateSearch:{ ' ' }
424+ < span className = "text-emerald-300" > projectSearchSchema</ span > ,
425+ </ p >
426+ < p className = "whitespace-nowrap pl-4" >
427+ loader: ({ '{' } context, params { '}' } ) =>
428+ </ p >
429+ < p className = "whitespace-nowrap pl-8" >
430+ context.queryClient.ensureQueryData(projectQuery(params.id)),
431+ </ p >
432+ < p className = "whitespace-nowrap" > { '})' } </ p >
433+ < p className = "mt-3 whitespace-nowrap text-zinc-400" >
434+ < span className = "text-pink-300" > const</ span > { ' ' }
435+ < span className = "text-cyan-300" > getProject</ span > =
436+ createServerFn({ '{' } method:{ ' ' }
437+ < span className = "text-emerald-300" > 'GET'</ span > { '}' } )
438+ </ p >
439+ < p className = "whitespace-nowrap pl-4 text-zinc-400" >
440+ .handler(({ '{' } data { '}' } ) => db.project.find(data.id))
441+ </ p >
442+ </ div >
443+ </ div >
444+
445+ < div className = "rounded-lg bg-cyan-50 p-4 dark:bg-cyan-950/30" >
446+ < div className = "flex items-center justify-between gap-3" >
447+ < p className = "text-sm font-black text-cyan-950 dark:text-cyan-100" >
448+ Request trace
449+ </ p >
450+ < span className = "inline-flex items-center gap-1.5 rounded-md bg-white px-1.5 py-0.5 text-[0.65rem] font-black uppercase text-cyan-800 dark:bg-cyan-950 dark:text-cyan-200" >
451+ < span className = "h-1.5 w-1.5 rounded-full bg-cyan-500 motion-safe:animate-pulse" />
452+ streaming
453+ </ span >
454+ </ div >
455+
456+ < div className = "mt-4 grid gap-3" >
457+ { startRuntimeSteps . map ( ( step , index ) => {
458+ const isLastStep = index === startRuntimeSteps . length - 1
459+
460+ return (
461+ < div
462+ key = { step . label }
463+ className = "grid grid-cols-[2rem_1fr] gap-3"
464+ >
465+ < span className = "flex h-8 w-8 items-center justify-center rounded-lg border border-cyan-200 bg-white text-xs font-black text-cyan-800 dark:border-cyan-800 dark:bg-zinc-950 dark:text-cyan-200" >
466+ { index + 1 }
467+ </ span >
468+ < div
469+ className = {
470+ isLastStep
471+ ? 'min-w-0'
472+ : 'min-w-0 border-b border-cyan-200/70 pb-3 dark:border-cyan-800/70'
473+ }
474+ >
475+ < div className = "flex min-w-0 items-start justify-between gap-3" >
476+ < p className = "text-sm font-black leading-5 text-zinc-950 dark:text-white" >
477+ { step . label }
478+ </ p >
479+ < p className = "min-w-0 truncate font-mono text-[0.65rem] font-bold text-cyan-800/80 dark:text-cyan-100/75" >
480+ { step . file }
481+ </ p >
482+ </ div >
483+ < p className = "mt-1 text-xs leading-5 text-cyan-950/75 dark:text-cyan-100/75" >
484+ { step . body }
485+ </ p >
486+ </ div >
487+ </ div >
488+ )
489+ } ) }
490+ </ div >
395491 </ div >
396- < p className = "mt-2 text-sm font-black leading-5 text-zinc-950 dark:text-white" >
397- { activeContract . title }
398- </ p >
399- < p className = "mt-1 text-xs leading-5 text-cyan-950/75 dark:text-cyan-100/75" >
400- { activeContract . body }
401- </ p >
402492 </ div >
403493
404- < div className = "mt-4 space-y-2" >
405- { routeContracts . map ( ( contract , index ) => (
406- < button
407- key = { contract . file }
408- aria-pressed = { activeContractIndex === index }
409- className = {
410- activeContractIndex === index
411- ? 'w-full rounded-lg border border-cyan-500 bg-cyan-500 px-3 py-2.5 text-left text-white'
412- : 'w-full rounded-lg border border-zinc-100 bg-zinc-50 px-3 py-2.5 text-left transition-colors hover:border-cyan-300 dark:border-zinc-800 dark:bg-zinc-900 dark:hover:border-cyan-800'
413- }
414- type = "button"
415- onClick = { ( ) => setActiveContractIndex ( index ) }
494+ < div className = "mt-4 grid gap-3 sm:grid-cols-3" >
495+ { startRuntimeOutputs . map ( ( output ) => (
496+ < div
497+ key = { output . label }
498+ className = "rounded-lg border border-zinc-100 bg-zinc-50 px-3 py-2.5 dark:border-zinc-800 dark:bg-zinc-900"
416499 >
417- < div className = "flex min-w-0 items-start justify-between gap-3" >
418- < p
419- className = {
420- activeContractIndex === index
421- ? 'min-w-0 truncate font-mono text-xs font-bold text-white'
422- : 'min-w-0 truncate font-mono text-xs font-bold text-zinc-900 dark:text-zinc-100'
423- }
424- >
425- { contract . file }
426- </ p >
427- < span
428- className = {
429- activeContractIndex === index
430- ? 'shrink-0 rounded-md bg-white/20 px-1.5 py-0.5 text-[0.65rem] font-black uppercase leading-none text-white'
431- : 'shrink-0 rounded-md bg-cyan-100 px-1.5 py-0.5 text-[0.65rem] font-black uppercase leading-none text-cyan-800 dark:bg-cyan-950 dark:text-cyan-200'
432- }
433- >
434- { contract . tag }
435- </ span >
436- </ div >
437- < p
438- className = {
439- activeContractIndex === index
440- ? 'mt-1.5 text-sm font-black leading-5 text-white'
441- : 'mt-1.5 text-sm font-black leading-5 text-zinc-950 dark:text-white'
442- }
443- >
444- { contract . title }
500+ < p className = "text-xs font-black text-zinc-950 dark:text-white" >
501+ { output . label }
445502 </ p >
446- < p
447- className = {
448- activeContractIndex === index
449- ? 'mt-1 text-xs leading-5 text-white/80'
450- : 'mt-1 text-xs leading-5 text-zinc-600 dark:text-zinc-400'
451- }
452- >
453- { contract . body }
503+ < p className = "mt-1 text-xs leading-5 text-zinc-600 dark:text-zinc-400" >
504+ { output . value }
454505 </ p >
455- </ button >
506+ </ div >
456507 ) ) }
457508 </ div >
458-
459- < div className = "mt-3 rounded-lg bg-cyan-50 px-3 py-2 text-xs leading-5 text-cyan-950 dark:bg-cyan-950/40 dark:text-cyan-100" >
460- < span className = "font-black" > Result:</ span > a Router app first; SSR,
461- streaming, APIs, middleware, and deployment added where the app needs
462- them.
463- </ div >
464509 </ div >
465510 )
466511}
0 commit comments