Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/santiagonieto09/portafolio/llms.txt

Use this file to discover all available pages before exploring further.

TanStack Start uses Nitro to execute the full React 19 component tree on the server before a single byte of HTML reaches the browser. Search engines index a complete, data-populated page on first crawl. Social network crawlers receive Open Graph and Twitter Card metadata with the correct title, description, and avatar image. No client-side JavaScript execution is required for any of this — the rendered HTML is the source of truth.

Head metadata

The head() export in src/routes/index.tsx is evaluated on the server during SSR and injects all metadata into <head> before the response is sent.

Title and description

<title>Santiago Nieto — Portafolio de desarrollo de software</title>
<meta name="description" content="Portafolio profesional de Santiago Nieto: proyectos, tecnologías, releases y estadísticas sincronizadas automáticamente desde GitHub." />

Open Graph

PropertyValue
og:titlePage title
og:descriptionPage description
og:typeprofile
og:url/
og:imageGitHub avatar URL (https://avatars.githubusercontent.com/u/91501165?v=4)

Twitter Card

NameValue
twitter:cardsummary_large_image
twitter:titlePage title
twitter:descriptionPage description
twitter:imageGitHub avatar URL
<link rel="canonical" href="/" />
The canonical tag prevents duplicate-content penalties when the site is accessible from multiple origins (e.g. the Vercel preview URL and the custom domain).

JSON-LD structured data

A <script type="application/ld+json"> block is injected with a Person entity:
{
  "@context": "https://schema.org",
  "@type": "Person",
  "name": "Santiago Nieto",
  "alternateName": "santiagonieto09",
  "jobTitle": "Desarrollador de software",
  "address": {
    "@type": "PostalAddress",
    "addressCountry": "CO"
  },
  "image": "https://avatars.githubusercontent.com/u/91501165?v=4",
  "sameAs": ["https://github.com/santiagonieto09"],
  "knowsAbout": ["Java", "Spring Boot", "Flutter", "Angular", "Python", "TypeScript"]
}
This structured data enables Google’s Knowledge Panel and rich search results to display the developer’s profile, job title, country, and technology expertise directly in search results.

Sitemap

src/routes/sitemap[.]xml.ts handles GET /sitemap.xml as a server-side route handler. On each request it:
  1. Reads the SITE_URL environment variable. If set, that value is used as the canonical base URL (trailing slashes stripped). If not set, the base URL is derived from new URL(request.url).origin — so the sitemap is always self-consistent regardless of which domain it is served from.
  2. Builds a <url> entry for / with <changefreq>weekly</changefreq> and <priority>1.0</priority>.
  3. Returns the XML document with Content-Type: application/xml and Cache-Control: public, max-age=3600.
Submit /sitemap.xml to Google Search Console and Bing Webmaster Tools after the first deploy.

Data loader

The loader in src/routes/index.tsx pre-populates the React Query cache on the server so components render with full data immediately:
loader: ({ context }) => {
  context.queryClient.ensureQueryData(portfolioQueryOptions());
}
ensureQueryData checks whether the ['portfolio'] query is already in the cache. On first render (SSR) it is not, so it calls getPortfolio() — a TanStack Start server function that executes in-process, not over HTTP — and stores the result. On subsequent renders within the same server process (if the QueryClient is reused), it returns the cached value immediately. The staleTime: 1000 * 60 * 60 option (1 hour) in portfolioQueryOptions() means the client will not trigger a background refetch for 60 minutes after hydration, even though ensureQueryData considers data “available” the moment it exists in the cache.

React Query integration

portfolioQueryOptions() in src/lib/portfolio.queries.ts centralises the query configuration used by both the loader and the component:
OptionValuePurpose
queryKey['portfolio']Unique cache key; used for invalidation and dehydration.
queryFn() => getPortfolio()Calls the TanStack Start server function.
staleTime1000 * 60 * 60 (1 h)Client-side freshness window; prevents unnecessary refetches.
gcTime1000 * 60 * 60 * 24 (24 h)How long unused cache entries are kept in memory before garbage collection.
The same portfolioQueryOptions() object is passed to both context.queryClient.ensureQueryData() in the loader and useSuspenseQuery() in the component, guaranteeing they reference the same cache key and configuration.
TanStack Start uses useSuspenseQuery on the portfolio page. Because the loader calls ensureQueryData before React begins rendering, the query cache already contains the snapshot when the component tree runs — both on the server and after hydration on the client. The Suspense boundary is never in a pending state during SSR, so the page always streams fully rendered HTML with no loading skeleton or fallback content visible to crawlers or users.

Build docs developers (and LLMs) love