From b0e48e37b60540433e0f99ce2b374ec56576e8a3 Mon Sep 17 00:00:00 2001 From: shrutikapoor08 Date: Mon, 5 May 2025 02:20:13 -0700 Subject: [PATCH 01/11] Adding Tutorial for Reading and Writing a File --- docs/start/config.json | 15 + .../framework/react/reading-writing-file.md | 561 ++++++++++++++++++ 2 files changed, 576 insertions(+) create mode 100644 docs/start/framework/react/reading-writing-file.md diff --git a/docs/start/config.json b/docs/start/config.json index 5aa7af5324e..76cae14ab80 100644 --- a/docs/start/config.json +++ b/docs/start/config.json @@ -209,6 +209,21 @@ ] } ] + }, + { + "label": "Tutorials", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "Reading and Writing a File", + "to": "framework/react/reading-writing-file" + } + ] + } + ] } ] } diff --git a/docs/start/framework/react/reading-writing-file.md b/docs/start/framework/react/reading-writing-file.md new file mode 100644 index 00000000000..0cf9334939d --- /dev/null +++ b/docs/start/framework/react/reading-writing-file.md @@ -0,0 +1,561 @@ +--- +id: reading-and-writing-file +title: Building a Full Stack DevJokes App with TanStack Start +--- + +This tutorial will guide you through building a complete full-stack application using TanStack Start. You'll create a DevJokesapp where users can view and add developer-themed jokes, demonstrating key concepts of TanStack Start including server functions, file-based data storage, and React components. + +Here's a demo of the app in action: + + +The complete code for this tutorial is available on [GitHub](https://github.com/shrutikapoor08/devjokes). + + +## What You'll Learn +1. Setting up a TanStack Start project +2. Implementing server functions +3. Reading and writing data to files +4. Building a complete UI with React components +5. Using TanStack Router for data fetching and navigation + + +## Prerequisites + +- Basic knowledge of React and TypeScript. +- Node.js and `pnpm` installed on your machine + + +## Nice to know +- [Server Side Rendering (SSR)](https://tanstack.com/router/latest/docs/framework/react/guide/ssr) +- [TanStack Router concepts](https://tanstack.com/router/latest/docs/framework/react/routing/routing-concepts) +- [React Query concepts](https://tanstack.com/query/latest/docs/framework/react/overview) + +## Setting up a TanStack Start Project + +First, let's create a new TanStack Start project: + +```bash +pnpx create-start-app devjokes +cd devjokes +``` + +When this script runs, it will ask you a few setup questions. You can either pick choices that work for you or just press enter to accept the defaults. + +Optionally, you can pass in a `--add-on` flag to get options such as Shadcn, Clerk, Convex, TanStack Query, etc. + +Once setup is complete, install dependencies and start the development server: + +```bash +pnpm i +pnpm dev +``` + +For this project, we'll need a few additional packages: + +```bash +# Install uuid for generating unique IDs +pnpm add uuid +pnpm add -D @types/uuid +``` + +## Understanding the project structure + +At this point, the project structure should look like this - + +``` +/devjokes +├── src/ +│ ├── routes/ +│ │ ├── __root.tsx # Root layout +│ │ ├── index.tsx # Home page +│ │ ├── demo.start.server-funcs.tsx # Demo server functions +│ │ └── demo.start.api-request.tsx # Demo API request +│ ├── api/ # API endpoints +│ ├── components/ # React components +│ ├── api.ts # API handler. +│ ├── client.tsx # Client entry point +│ ├── router.tsx # Router configuration +│ ├── routeTree.gen.ts # Generated route tree +│ ├── ssr.tsx # Server-side rendering +│ └── styles.css # Global styles +├── public/ # Static assets +├── app.config.ts # TanStack Start configuration +├── package.json # Project dependencies +└── tsconfig.json # TypeScript configuration +``` + +This structure might seem overwhelming at first, but here are the key files you need to focus on: +1. `router.tsx` - Sets up routing for your application +2. `src/routes/__root.tsx` - The root layout component where you can add global styles and components +3. `src/routes/index.tsx` - Your home page +4. `client.tsx` - Client-side entry point +5. `ssr.tsx` - Handles server-side rendering + + +Once your project is set up, you can access your app at `localhost:3000`. You should see the default TanStack Start welcome page. + +At this point, your app will look like this - + +![TanStack Start Welcome Page After Setup](https://res.cloudinary.com/dubc3wnbv/image/upload/v1746312482/Photo-1_lpechn.png) + +## Step 1: Reading Data From a File + +Let's start by creating a file-based storage system for our jokes. + +### Step 1.1: Create a JSON File with Jokes +Let's set up a list of jokes that we can use to render on the page. Create a `data` directory in your project root and a `jokes.json` file within it: + +```bash +mkdir -p src/data +touch src/data/jokes.json +``` + +Now, let's add some sample jokes to this file: + +```json +[ + { + "id": "1", + "question": "Why don't keyboards sleep?", + "answer": "Because they have two shifts" + }, + { + "id": "2", + "question": "Are you a RESTful API?", + "answer": "Because you GET my attention, PUT some love, POST the cutest smile, and DELETE my bad day" + }, + { + "id": "3", + "question": "I used to know a joke about Java", + "answer": "But I ran out of memory." + }, + { + "id": "4", + "question": "Why do Front-End Developers eat lunch alone?", + "answer": "Because, they don't know how to join tables." + }, + { + "id": "5", + "question": "I am declaring a war.", + "answer": "var war;" + } +] +``` + +### Step 1.2: Create Types for Our Data +Let's create a file to define our data types. Create a new file at `src/types/index.ts`: + +```typescript +// src/types/index.ts +export interface Joke { + id: string; + question: string; + answer: string; +} + +export type JokesData = Joke[]; +``` + + ### Step 1.3: Create Server Functions to Read the File + + Let's create a new file `src/serverActions/jokesActions.ts` to create a server function to perform a read-write operation. We will be creating a server function using [`createServerFn`](https://tanstack.com/start/latest/docs/framework/react/server-functions). + + +```tsx +// src/serverActions/jokesActions.ts + +import { createServerFn } from "@tanstack/react-start"; +import * as fs from "node:fs"; +import type { JokesData } from "../types"; + +const JOKES_FILE = "src/data/jokes.json"; + +export const getJokes = createServerFn({ method: "GET" }).handler(async () => { + const jokes = await fs.promises.readFile(JOKES_FILE, "utf-8"); + return JSON.parse(jokes) as JokesData; +}); + +``` + +In this code, we are using `createServerFn` to create a server function that reads the jokes from the JSON file. The `handler` function is where we are using the `fs` module to read the file. + + +### Step 1.4: Consume Server Function on the Client Side + +Now to consume this server function, we can simply call it in our code using TanStack Router which already comes with TanStack Start! + +Now let's create a new component `JokesList` to render the jokes on the page with a little Tailwind styling sprinkle. + +```tsx +// src/components/JokesList.tsx +import { Joke } from "../types"; + +interface JokesListProps { + jokes: Joke[]; +} + +export function JokesList({ jokes }: JokesListProps) { + if (!jokes || jokes.length === 0) { + return

No jokes found. Add some!

; + } + + return ( +
+

Jokes Collection

+ {jokes.map((joke) => ( +
+

{joke.question}

+

{joke.answer}

+
+ ))} +
+ ); +} +``` + +Now let's call our server function inside `App.jsx` using TanStack Router which already comes with TanStack Start! + +```jsx +// App.jsx +import { createFileRoute } from "@tanstack/react-router"; +import { getJokes } from "./serverActions/jokesActions"; +import { JokesList } from "./JokesList"; + +export const Route = createFileRoute("/")({ + loader: async () => { + // Load jokes data when the route is accessed + return getJokes(); + }, + component: App, +}); + +const App = () => { + const jokes = Route.useLoaderData() || []; + + return ( +
+

DevJokes

+ +
+ ); +}; +``` +When the page loads, `jokes` will have data from the `jokes.json` file already! + + +With a little Tailwind styling, the app should look like this: + +![DevJoke App with 5 DevJokes](https://res.cloudinary.com/dubc3wnbv/image/upload/v1746313558/Screenshot_2025-05-03_at_4.04.50_PM_w0eern.png) + +## Step 2: Writing Data to a File + +So far, we have been able to read from the file successfully! We can use the same approach to write to the `jokes.json` file using `createServerFunction`. + +### Step 2.1: Create Server Function to Write to the File + +It's time to modify the `jokes.json` file so that we can add new jokes to it. Let's create another server function but this time with a `POST` method to write to the same file. + +```tsx +// src/serverActions/jokesActions.ts +import { createServerFn } from "@tanstack/react-start"; +import * as fs from "node:fs"; +import { v4 as uuidv4 } from 'uuid'; // Add this import +import type { Joke, JokesData } from "../types"; + +export const addJoke = createServerFn({ method: "POST" }) +.validator((data: { question: string; answer: string }) => { + // Validate input data + if (!data.question || !data.question.trim()) { + throw new Error("Joke question is required"); + } + if (!data.answer || !data.answer.trim()) { + throw new Error("Joke answer is required"); + } + return data; + }) .handler(async ({ data }) => { + try { + // Read the existing jokes from the file + const jokesData = await getJokes(); + + // Create a new joke with a unique ID + const newJoke: Joke = { + id: uuidv4(), + question: data.question, + answer: data.answer, + }; + + // Add the new joke to the list + const updatedJokes = [...jokesData, newJoke]; + + // Write the updated jokes back to the file + await fs.promises.writeFile( + JOKES_FILE, + JSON.stringify(updatedJokes, null, 2), + "utf-8" + ); + + return newJoke; + } catch (error) { + console.error("Failed to add joke:", error); + throw new Error("Failed to add joke"); + } + }); + +``` + +In this code: +- We are using `createServerFn` to create server functions that run on the server but can be called from the client. This server function is used to write data to the file. +- We are going to first use `validator` to validate the input data. This is a good practice to ensure that the data we are receiving is in the correct format. +- We are going to perform the actual write operation in the `handler` function. +- `getJokes` reads the jokes from our JSON file. +- `addJoke` validates the input data and adds a new joke to our file. +- We're using `uuidv4()` to generate unique IDs for our jokes. + + +### Step 2.2: Adding a Form to Add Jokes to our JSON File + +Now, let's modify our home page to display jokes and provide a form to add new ones. Let's create a new component called `JokeForm.jsx` and add the following form to it: + +```tsx +// src/components/JokeForm.tsx +import { useState } from "react"; +import { useRouter } from "@tanstack/react-router"; +import { addJoke } from "../serverActions/jokesActions"; + +export function JokeForm() { + const router = useRouter(); + const [question, setQuestion] = useState(""); + const [answer, setAnswer] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState(null); + + return ( +
+ {error && ( +
{error}
+ )} + +
+ setQuestion(e.target.value)} + required + /> + + setAnswer(e.target.value)} + required + /> + + +
+
+ ); +} + +``` +### Step 2.3: Wire Up the Form to the Server Function +Now, let's wire the form up to our `addJoke` server function in the `handleSubmit` function. Calling a server action is simple! It's just a function call. + + +```tsx +//JokeForm.tsx +import { useState } from "react"; +import { addJoke } from "../serverActions/jokesActions"; +import { useRouter } from "@tanstack/react-router"; + +export function JokeForm() { + const router = useRouter(); + const [question, setQuestion] = useState(""); + const [answer, setAnswer] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState(null); + + + const handleSubmit = async () => { + if (!question || !answer || isSubmitting) return; + try { + setIsSubmitting(true); + await addJoke({ + data: { question, answer }, + }); + + // Clear form + setQuestion(""); + setAnswer(""); + + // Refresh data + router.invalidate(); + } catch (error) { + console.error("Failed to add joke:", error); + setError("Failed to add joke"); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+ {error && ( +
{error}
+ )} + setQuestion(e.target.value)} + value={question} + /> + setAnswer(e.target.value)} + value={answer} + /> + +
+ ); +} + +``` + +With this, our UI should look like this: +![DevJoke App with Form to Add Jokes](https://res.cloudinary.com/dubc3wnbv/image/upload/v1746356983/Screenshot_2025-05-04_at_4.06.57_AM_dkgvsm.png) + + + +## Understanding How It All Works Together + +Let's break down how the different parts of our application work together: + +1. **Server Functions**: These run on the server and handle data operations + - `getJokes`: Reads the jokes from our JSON file + - `addJoke`: Adds a new joke to our JSON file + +2. **TanStack Router**: Handles routing and data loading + - The loader function fetches jokes data when the route is accessed + - `useLoaderData` makes this data available in our component + - `router.invalidate()` refreshes the data when we add a new joke + +3. **React Components**: Build the UI of our application + - `JokesList`: Displays the list of jokes + - `JokeForm`: Provides a form for adding new jokes + +4. **File-Based Storage**: Stores our jokes in a JSON file + - Reading and writing are handled by Node.js `fs` module + - Data is persisted between server restarts + + + +## How Data Flows Through the Application + +### Data Flow + +![Data Flow Diagram](https://res.cloudinary.com/dubc3wnbv/image/upload/v1746437057/Screenshot_2025-05-05_at_2.23.11_AM_wxfz2m.png) + +When a user visits the home page: +1. The `loader` function in the route calls `getJokes()` server function +2. The server reads `jokes.json` and returns the jokes data +3. This data is passed to the `HomePage` component through `useLoaderData()` +4. The `HomePage` component passes the data to the `JokesList` component + +When a user adds a new joke: +1. They fill out the form and submit it +2. The `handleSubmit` function calls the `addJoke()` server function +3. The server reads the current jokes, adds the new joke, and writes the updated data back to `jokes.json` +4. After the operation completes, we call `router.invalidate()` to refresh the data +5. This triggers the loader again, fetching the updated jokes +6. The UI updates to show the new joke in the list + + +Here's a demo of the app in action: + + + + +## Common Issues and Debugging + +Here are some common issues you might encounter when building your TanStack Start application and how to resolve them: + +### Server Functions Not Working + +If your server functions aren't working as expected: + +1. Check that you're using the correct HTTP method (`GET`, `POST`, etc.) +2. Ensure file paths are correct and accessible to the server +3. Check the server console for error messages +4. Make sure you're not using client-only APIs in server functions + +### Route Data Not Loading + +If route data isn't loading properly: + +1. Verify that your loader function is properly implemented +2. Check that you're using `useLoaderData()` correctly +3. Look for errors in the browser console +4. Make sure your server function is working correctly + +### Form Submission Issues + +If form submissions aren't working: + +1. Check for validation errors in your server function +2. Verify that form event prevention (`e.preventDefault()`) is working +3. Make sure state updates are happening correctly +4. Look for network errors in the browser's Developer Tools + +### File Reading/Writing Issues + +When working with file-based storage: + +1. Ensure file paths are correct +2. Check file permissions +3. Make sure you're handling async operations properly with `await` +4. Add proper error handling for file operations + +## Conclusion + +Congratulations! You've built a full-stack DevJokes app using TanStack Start. In this tutorial, you've learned: + +- How to set up a TanStack Start project +- How to implement server functions for data operations +- How to read and write data to files +- How to build React components for your UI +- How to use TanStack Router for routing and data fetching + +This simple application demonstrates the power of TanStack Start for building full-stack applications with a minimal amount of code. You can extend this app by adding features like: + +- Joke categories +- Ability to edit and delete jokes +- User authentication +- Voting for favorite jokes + +The complete code for this tutorial is available on [GitHub](https://github.com/shrutikapoor08/devjokes). \ No newline at end of file From 325ee0d652a763232acd2b3ab316bec60743ffbc Mon Sep 17 00:00:00 2001 From: shrutikapoor08 Date: Fri, 29 Aug 2025 17:04:00 -0700 Subject: [PATCH 02/11] Adding fetching api doc --- .../framework/react/fetching-external-api.md | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 docs/start/framework/react/fetching-external-api.md diff --git a/docs/start/framework/react/fetching-external-api.md b/docs/start/framework/react/fetching-external-api.md new file mode 100644 index 00000000000..ce7462ef432 --- /dev/null +++ b/docs/start/framework/react/fetching-external-api.md @@ -0,0 +1,269 @@ +--- +id: reading-and-writing-file +title: Building a Full Stack DevJokes App with TanStack Start +--- + + This guide demonstrates how to integrate external API calls into your TanStack Start application using route loaders. We will use TMDB API to fetch popular movies using TanStack Start and understand how to fetch data in a TanStack Start app. + + +The complete code for this tutorial is available on [GitHub](https://github.com/shrutikapoor08/devjokes). + + +## What You'll Learn +1. Setting up external API integration with TanStack Start +1. Implementing route loaders for server-side data fetching +1. Building responsive UI components with fetched data +1. Handling loading states and error management + +## Prerequisites + +- Basic knowledge of React and TypeScript +- Node.js and `pnpm` installed on your machine +- A TMDB API key (free at themoviedb.org) + + +## Nice to know +- [TanStack Router](https://tanstack.com/router/latest/docs/framework/react/routing/routing-concepts) + +## Setting up a TanStack Start Project + +First, let's create a new TanStack Start project: + +```bash +pnpx create-start-app movie-discovery +cd movie-discovery +``` + +When this script runs, it will ask you a few setup questions. You can either pick choices that work for you or just press enter to accept the defaults. + +Optionally, you can pass in a `--add-on` flag to get options such as Shadcn, Clerk, Convex, TanStack Query, etc. + +Once setup is complete, install dependencies and start the development server: + +```bash +pnpm i +pnpm dev +``` + + +Once your project is set up, you can access your app at `localhost:3000`. You should see the default TanStack Start welcome page. + +## Step 1: Setup a .env file with TMDB_AUTH_TOKEN +In order to fetch the API for fetching movies, we will need an `auth_token`. You can get this at https://www.themoviedb.org/. + +First, let's set up environment variables for our API key. Create a .env.local file in your project root: + +``` +touch .env.local + +``` + + +Add your TMDB API token to this file: +``` +TMDB_AUTH_TOKEN=your_bearer_token_here +``` + +*Important*: Make sure to add .env.local to your .gitignore file to keep your API keys secure. + + +## Step 2: Defining Data Types + +Let's create TypeScript interfaces for our movie data. Create a new file at `src/types/movie.ts`: + +```ts +// src/types/movie.ts +export interface Movie { + id: number + title: string + overview: string + poster_path: string | null + backdrop_path: string | null + release_date: string + vote_average: number + popularity: number +} + +export interface TMDBResponse { + page: number + results: Movie[] + total_pages: number + total_results: number +} +``` + +## Step 3: Creating the API Fetch Function +Now let's create a function to fetch popular movies from the TMDB API. Create a new file at src/routes/fetch-movies.tsx: + + +```ts +const API_URL = 'https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc' + +async function fetchPopularMovies(): Promise { + const response = await fetch( + API_URL, + { + headers: { + 'accept': 'application/json', + 'Authorization': `Bearer ${process.env.TMDB_AUTH_TOKEN}`, + }, + } + ) + + if (!response.ok) { + throw new Error(`Failed to fetch movies: ${response.statusText}`) + } + + return response.json() +} + +``` + +## Step 4: Creating the Route with Loader +Now let's setup our route that uses loader to fetch data. + +```tsx +export const Route = createFileRoute('/fetch-movies')({ + component: MoviesPage, + loader: async () => { + try { + const moviesData = await fetchPopularMovies() + return { movies: moviesData.results, error: null } + } catch (error) { + console.error('Error fetching movies:', error) + return { movies: [], error: 'Failed to load movies' } + } + }, +}) + + +``` + +## Step 5: Consuming loader data in MoviesPage component + +Finally, lets consume the movies data by calling `Route.useLoaderData()` + +```jsx + +const MoviesPage = () => { + const { movies, error } = Route.useLoaderData() + + return ( +
+
+

Popular Movies

+ + {error && ( +
+ {error} +
+ )} + + {movies.length > 0 ? ( +
+ {movies.slice(0, 12).map((movie) => ( + + ))} +
+ ) : ( + !error && ( +
+ Loading movies... +
+ ) + )} +
+
+ ) +} + +//MovieCard component +const MovieCard = ({ movie }: { movie: Movie }) => { + return ( +
+ {movie.poster_path && ( + {movie.title} + )} +
+ +
+
+ ) +} + +//MovieDetails component +const MovieDetails = ({ movie }: { movie: Movie }) => { + return ( + <> +

+ {movie.title} +

+

+ {movie.overview} +

+
+ {movie.release_date} + + ⭐️ {movie.vote_average.toFixed(1)} + +
+ + ) +} + + + +``` + +So far, we have done the following - +1. We're using `createFileRoute` to create a route with a loader +2. The loader function runs on the server during SSR and fetches our movie data +3. We're handling errors gracefully by returning an error state +4. The component uses `Route.useLoaderData()` to access the fetched data +5. We're implementing proper accessibility features with ARIA labels + + + + + +## Understanding How It All Works Together +Let's break down how the different parts of our application work together: + +1. Route Loader: When a user visits /demo/start/fetch-api, the loader function runs +2. API Call: The loader calls fetchPopularMovies() which makes an HTTP request to TMDB +3. Server-Side Rendering: The data is fetched on the server, ensuring it's available immediately +4. Component Rendering: The MoviesPage component receives the data via `useLoaderData()` +5. UI Display: The movie cards are rendered with the fetched data + +## Why This is Good +1. Server Rendered: Movies are rendered on the server, making them visible to search engines and reducing load on the client side. +2. Fast Loading: Data is available immediately when the page loads +3. Error Handling: Graceful handling of API failures +4. Type Safety: Full TypeScript support throughout the data flow + +## Step 6: Testing Your Application +If everything worked correctly, you should be able to see the popular movies + + + + + +Next Step - Making movie details inteactive and imporoving data handling using TanStack Query \ No newline at end of file From 73a4ff7c6a11897a1d92564374ba313045ca6565 Mon Sep 17 00:00:00 2001 From: shrutikapoor08 Date: Fri, 29 Aug 2025 17:30:05 -0700 Subject: [PATCH 03/11] Updating blog post --- .../framework/react/fetching-external-api.md | 187 ++++++++++-------- 1 file changed, 100 insertions(+), 87 deletions(-) diff --git a/docs/start/framework/react/fetching-external-api.md b/docs/start/framework/react/fetching-external-api.md index ce7462ef432..93da5570f6b 100644 --- a/docs/start/framework/react/fetching-external-api.md +++ b/docs/start/framework/react/fetching-external-api.md @@ -1,12 +1,12 @@ --- -id: reading-and-writing-file -title: Building a Full Stack DevJokes App with TanStack Start +id: fetching-external-api +title: Calling an external API using TanStack Start --- This guide demonstrates how to integrate external API calls into your TanStack Start application using route loaders. We will use TMDB API to fetch popular movies using TanStack Start and understand how to fetch data in a TanStack Start app. - -The complete code for this tutorial is available on [GitHub](https://github.com/shrutikapoor08/devjokes). + +The complete code for this tutorial is available on [GitHub](https://github.com/shrutikapoor08/tanstack-start-movies). ## What You'll Learn @@ -19,7 +19,7 @@ The complete code for this tutorial is available on [GitHub](https://github.com/ - Basic knowledge of React and TypeScript - Node.js and `pnpm` installed on your machine -- A TMDB API key (free at themoviedb.org) +- A TMDB API key (free at https://themoviedb.org) ## Nice to know @@ -45,26 +45,47 @@ pnpm i pnpm dev ``` +## Understanding the Project Structure + +At this point, the project structure should look like this: +``` +/movie-discovery +├── src/ +│ ├── routes/ +│ │ ├── __root.tsx # Root layout +│ │ ├── index.tsx # Home page +│ │ └── fetch-movies.tsx # Movie fetching route +│ ├── types/ +│ │ └── movie.ts # Movie type definitions +│ ├── router.tsx # Router configuration +│ ├── routeTree.gen.ts # Generated route tree +│ └── styles.css # Global styles +├── public/ # Static assets +├── app.config.ts # TanStack Start configuration +├── package.json # Project dependencies +└── tsconfig.json # TypeScript configuration + +``` + +Once your project is set up, you can access your app at localhost:3000. You should see the default TanStack Start welcome page. -Once your project is set up, you can access your app at `localhost:3000`. You should see the default TanStack Start welcome page. ## Step 1: Setup a .env file with TMDB_AUTH_TOKEN -In order to fetch the API for fetching movies, we will need an `auth_token`. You can get this at https://www.themoviedb.org/. +To fetch movies from the TMDB API, you need an authentication token. You can get this for free at themoviedb.org. -First, let's set up environment variables for our API key. Create a .env.local file in your project root: +First, let's set up environment variables for our API key. Create a `.env` file in your project root: ``` -touch .env.local +touch .env ``` - Add your TMDB API token to this file: ``` TMDB_AUTH_TOKEN=your_bearer_token_here ``` -*Important*: Make sure to add .env.local to your .gitignore file to keep your API keys secure. +*Important*: Make sure to add `.env` to your `.gitignore` file to keep your API keys secure. ## Step 2: Defining Data Types @@ -92,11 +113,17 @@ export interface TMDBResponse { } ``` -## Step 3: Creating the API Fetch Function -Now let's create a function to fetch popular movies from the TMDB API. Create a new file at src/routes/fetch-movies.tsx: +## Step 3: Creating the Route with API Fetch Function +Now let's create our route that fetches data from the TMDB API. Create a new file at `src/routes/fetch-movies.tsx`: + + +```typescript + +// src/routes/fetch-movies.tsx +import { createFileRoute } from '@tanstack/react-router' +import type { Movie, TMDBResponse } from '../types/movie' -```ts const API_URL = 'https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc' async function fetchPopularMovies(): Promise { @@ -117,12 +144,7 @@ async function fetchPopularMovies(): Promise { return response.json() } -``` - -## Step 4: Creating the Route with Loader -Now let's setup our route that uses loader to fetch data. -```tsx export const Route = createFileRoute('/fetch-movies')({ component: MoviesPage, loader: async () => { @@ -136,57 +158,15 @@ export const Route = createFileRoute('/fetch-movies')({ }, }) - ``` -## Step 5: Consuming loader data in MoviesPage component +## Step 4: Building the Movie Components -Finally, lets consume the movies data by calling `Route.useLoaderData()` +Now let's create the components that will display our movie data. Add these components to the same `fetch-movies.tsx` file: ```jsx -const MoviesPage = () => { - const { movies, error } = Route.useLoaderData() - - return ( -
-
-

Popular Movies

- - {error && ( -
- {error} -
- )} - - {movies.length > 0 ? ( -
- {movies.slice(0, 12).map((movie) => ( - - ))} -
- ) : ( - !error && ( -
- Loading movies... -
- ) - )} -
-
- ) -} - -//MovieCard component +// MovieCard component const MovieCard = ({ movie }: { movie: Movie }) => { return (
{ ) } -//MovieDetails component +// MovieDetails component const MovieDetails = ({ movie }: { movie: Movie }) => { return ( <> @@ -228,42 +208,75 @@ const MovieDetails = ({ movie }: { movie: Movie }) => { ) } +``` +## Step 5: Creating the MoviesPage Component +Finally, let's create the main component that consumes the loader data: +```jsx +// MoviesPage component +const MoviesPage = () => { + const { movies, error } = Route.useLoaderData() -``` - -So far, we have done the following - -1. We're using `createFileRoute` to create a route with a loader -2. The loader function runs on the server during SSR and fetches our movie data -3. We're handling errors gracefully by returning an error state -4. The component uses `Route.useLoaderData()` to access the fetched data -5. We're implementing proper accessibility features with ARIA labels + return ( +
+
+

Popular Movies

+ {error && ( +
+ {error} +
+ )} + {movies.length > 0 ? ( +
+ {movies.slice(0, 12).map((movie) => ( + + ))} +
+ ) : ( + !error && ( +
+ Loading movies... +
+ ) + )} +
+
+ ) +} +``` -## Understanding How It All Works Together +### Understanding How It All Works Together Let's break down how the different parts of our application work together: -1. Route Loader: When a user visits /demo/start/fetch-api, the loader function runs -2. API Call: The loader calls fetchPopularMovies() which makes an HTTP request to TMDB -3. Server-Side Rendering: The data is fetched on the server, ensuring it's available immediately -4. Component Rendering: The MoviesPage component receives the data via `useLoaderData()` -5. UI Display: The movie cards are rendered with the fetched data +1. Route Loader: When a user visits `/fetch-movies`, the loader function runs on the server +2. API Call: The loader calls `fetchPopularMovies()` which makes an HTTP request to TMDB +3. Server-Side Rendering: The data is fetched on the server reducing the load on the client side +4. Component Rendering: The `MoviesPage` component receives the data via `Route.useLoaderData()` +5. UI Loads: The movie cards are rendered with the fetched data -## Why This is Good -1. Server Rendered: Movies are rendered on the server, making them visible to search engines and reducing load on the client side. -2. Fast Loading: Data is available immediately when the page loads -3. Error Handling: Graceful handling of API failures -4. Type Safety: Full TypeScript support throughout the data flow ## Step 6: Testing Your Application -If everything worked correctly, you should be able to see the popular movies +Now you can test your application by visiting http://localhost:3000/fetch-movies. If everything is set up correctly, you should see a beautiful grid of popular movies with their posters, titles, and ratings. Your app should look like this: + +![Netflix style movie setup](https://res.cloudinary.com/dubc3wnbv/image/upload/v1756512946/Screenshot_2025-08-29_at_5.14.26_PM_iiex7o.png) - +## Conclusion +You've successfully built a movie discovery app that integrates with an external API using TanStack Start. This tutorial demonstrated how to use route loaders for server-side data fetching and building UI components with external data. -Next Step - Making movie details inteactive and imporoving data handling using TanStack Query \ No newline at end of file +While fetching data at build time in TanStack Start is perfect for static content like blog posts or product pages, it's not ideal for interactive apps. If you need features like real-time updates, caching, or infinite scrolling, you'll want to use TanStack Query on the client side instead. TanStack Query makes it easy to handle dynamic data with built-in caching, background updates, and smooth user interactions. By using TanStack Start for static content and TanStack Query for interactive features, you get fast loading pages plus all the modern functionality users expect. \ No newline at end of file From 95f9a4609405426d60ccccb27a8327e22d5f0c8d Mon Sep 17 00:00:00 2001 From: shrutikapoor08 Date: Fri, 29 Aug 2025 17:51:43 -0700 Subject: [PATCH 04/11] Adding route for the new doc --- docs/start/config.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/start/config.json b/docs/start/config.json index 76cae14ab80..d76315d9303 100644 --- a/docs/start/config.json +++ b/docs/start/config.json @@ -222,6 +222,15 @@ "to": "framework/react/reading-writing-file" } ] + }, + { + "label": "react", + "children": [ + { + "label": "Fetching data from an external API", + "to": "framework/react/fetching-external-api" + } + ] } ] } From 5d502553d5d42fbf559b632339398ea99b0579e5 Mon Sep 17 00:00:00 2001 From: shrutikapoor08 Date: Sat, 30 Aug 2025 00:46:01 -0700 Subject: [PATCH 05/11] Removing config.json changes --- docs/start/config.json | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/docs/start/config.json b/docs/start/config.json index d76315d9303..5aa7af5324e 100644 --- a/docs/start/config.json +++ b/docs/start/config.json @@ -209,30 +209,6 @@ ] } ] - }, - { - "label": "Tutorials", - "children": [], - "frameworks": [ - { - "label": "react", - "children": [ - { - "label": "Reading and Writing a File", - "to": "framework/react/reading-writing-file" - } - ] - }, - { - "label": "react", - "children": [ - { - "label": "Fetching data from an external API", - "to": "framework/react/fetching-external-api" - } - ] - } - ] } ] } From 7ca00e70e121b99d5a21e5329f647abf389f7656 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 08:22:41 +0000 Subject: [PATCH 06/11] ci: apply automated fixes --- .../framework/react/fetching-external-api.md | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/docs/start/framework/react/fetching-external-api.md b/docs/start/framework/react/fetching-external-api.md index 93da5570f6b..3126e964013 100644 --- a/docs/start/framework/react/fetching-external-api.md +++ b/docs/start/framework/react/fetching-external-api.md @@ -3,13 +3,12 @@ id: fetching-external-api title: Calling an external API using TanStack Start --- - This guide demonstrates how to integrate external API calls into your TanStack Start application using route loaders. We will use TMDB API to fetch popular movies using TanStack Start and understand how to fetch data in a TanStack Start app. - +This guide demonstrates how to integrate external API calls into your TanStack Start application using route loaders. We will use TMDB API to fetch popular movies using TanStack Start and understand how to fetch data in a TanStack Start app. The complete code for this tutorial is available on [GitHub](https://github.com/shrutikapoor08/tanstack-start-movies). - ## What You'll Learn + 1. Setting up external API integration with TanStack Start 1. Implementing route loaders for server-side data fetching 1. Building responsive UI components with fetched data @@ -21,8 +20,8 @@ The complete code for this tutorial is available on [GitHub](https://github.com/ - Node.js and `pnpm` installed on your machine - A TMDB API key (free at https://themoviedb.org) - ## Nice to know + - [TanStack Router](https://tanstack.com/router/latest/docs/framework/react/routing/routing-concepts) ## Setting up a TanStack Start Project @@ -48,6 +47,7 @@ pnpm dev ## Understanding the Project Structure At this point, the project structure should look like this: + ``` /movie-discovery ├── src/ @@ -69,8 +69,8 @@ At this point, the project structure should look like this: Once your project is set up, you can access your app at localhost:3000. You should see the default TanStack Start welcome page. - ## Step 1: Setup a .env file with TMDB_AUTH_TOKEN + To fetch movies from the TMDB API, you need an authentication token. You can get this for free at themoviedb.org. First, let's set up environment variables for our API key. Create a `.env` file in your project root: @@ -81,12 +81,12 @@ touch .env ``` Add your TMDB API token to this file: + ``` TMDB_AUTH_TOKEN=your_bearer_token_here ``` -*Important*: Make sure to add `.env` to your `.gitignore` file to keep your API keys secure. - +_Important_: Make sure to add `.env` to your `.gitignore` file to keep your API keys secure. ## Step 2: Defining Data Types @@ -114,37 +114,32 @@ export interface TMDBResponse { ``` ## Step 3: Creating the Route with API Fetch Function -Now let's create our route that fetches data from the TMDB API. Create a new file at `src/routes/fetch-movies.tsx`: +Now let's create our route that fetches data from the TMDB API. Create a new file at `src/routes/fetch-movies.tsx`: ```typescript - // src/routes/fetch-movies.tsx import { createFileRoute } from '@tanstack/react-router' import type { Movie, TMDBResponse } from '../types/movie' - -const API_URL = 'https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc' +const API_URL = + 'https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc' async function fetchPopularMovies(): Promise { - const response = await fetch( - API_URL, - { - headers: { - 'accept': 'application/json', - 'Authorization': `Bearer ${process.env.TMDB_AUTH_TOKEN}`, - }, - } - ) - + const response = await fetch(API_URL, { + headers: { + accept: 'application/json', + Authorization: `Bearer ${process.env.TMDB_AUTH_TOKEN}`, + }, + }) + if (!response.ok) { throw new Error(`Failed to fetch movies: ${response.statusText}`) } - + return response.json() } - export const Route = createFileRoute('/fetch-movies')({ component: MoviesPage, loader: async () => { @@ -157,7 +152,6 @@ export const Route = createFileRoute('/fetch-movies')({ } }, }) - ``` ## Step 4: Building the Movie Components @@ -211,6 +205,7 @@ const MovieDetails = ({ movie }: { movie: Movie }) => { ``` ## Step 5: Creating the MoviesPage Component + Finally, let's create the main component that consumes the loader data: ```jsx @@ -233,20 +228,31 @@ const MoviesPage = () => {

Popular Movies

{error && ( -
+
{error}
)} {movies.length > 0 ? ( -
+
{movies.slice(0, 12).map((movie) => ( ))}
) : ( !error && ( -
+
Loading movies...
) @@ -255,11 +261,10 @@ const MoviesPage = () => {
) } - ``` - ### Understanding How It All Works Together + Let's break down how the different parts of our application work together: 1. Route Loader: When a user visits `/fetch-movies`, the loader function runs on the server @@ -268,15 +273,14 @@ Let's break down how the different parts of our application work together: 4. Component Rendering: The `MoviesPage` component receives the data via `Route.useLoaderData()` 5. UI Loads: The movie cards are rendered with the fetched data - ## Step 6: Testing Your Application -Now you can test your application by visiting http://localhost:3000/fetch-movies. If everything is set up correctly, you should see a beautiful grid of popular movies with their posters, titles, and ratings. Your app should look like this: +Now you can test your application by visiting http://localhost:3000/fetch-movies. If everything is set up correctly, you should see a beautiful grid of popular movies with their posters, titles, and ratings. Your app should look like this: ![Netflix style movie setup](https://res.cloudinary.com/dubc3wnbv/image/upload/v1756512946/Screenshot_2025-08-29_at_5.14.26_PM_iiex7o.png) - ## Conclusion + You've successfully built a movie discovery app that integrates with an external API using TanStack Start. This tutorial demonstrated how to use route loaders for server-side data fetching and building UI components with external data. -While fetching data at build time in TanStack Start is perfect for static content like blog posts or product pages, it's not ideal for interactive apps. If you need features like real-time updates, caching, or infinite scrolling, you'll want to use TanStack Query on the client side instead. TanStack Query makes it easy to handle dynamic data with built-in caching, background updates, and smooth user interactions. By using TanStack Start for static content and TanStack Query for interactive features, you get fast loading pages plus all the modern functionality users expect. \ No newline at end of file +While fetching data at build time in TanStack Start is perfect for static content like blog posts or product pages, it's not ideal for interactive apps. If you need features like real-time updates, caching, or infinite scrolling, you'll want to use TanStack Query on the client side instead. TanStack Query makes it easy to handle dynamic data with built-in caching, background updates, and smooth user interactions. By using TanStack Start for static content and TanStack Query for interactive features, you get fast loading pages plus all the modern functionality users expect. From ba62cf066683b587cb9de8189ad0def7f3bacd8c Mon Sep 17 00:00:00 2001 From: shrutikapoor08 Date: Sun, 31 Aug 2025 15:49:33 -0700 Subject: [PATCH 07/11] Adding router in config.json --- docs/start/config.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/start/config.json b/docs/start/config.json index 02a44b30acc..b1f6fab62bc 100644 --- a/docs/start/config.json +++ b/docs/start/config.json @@ -242,6 +242,15 @@ "to": "framework/react/reading-writing-file" } ] + }, + { + "label": "react", + "children": [ + { + "label": "Fetching data from external API", + "to": "framework/react/fetching-external-api" + } + ] } ] } From d69efe05f7c0309c87b9ae2dfe58b267eda36c4c Mon Sep 17 00:00:00 2001 From: shrutikapoor08 Date: Sun, 31 Aug 2025 16:00:08 -0700 Subject: [PATCH 08/11] nit improvements --- .../framework/react/fetching-external-api.md | 59 +++++++++++-------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/docs/start/framework/react/fetching-external-api.md b/docs/start/framework/react/fetching-external-api.md index 3126e964013..2ea2887423e 100644 --- a/docs/start/framework/react/fetching-external-api.md +++ b/docs/start/framework/react/fetching-external-api.md @@ -17,12 +17,12 @@ The complete code for this tutorial is available on [GitHub](https://github.com/ ## Prerequisites - Basic knowledge of React and TypeScript -- Node.js and `pnpm` installed on your machine -- A TMDB API key (free at https://themoviedb.org) +- Node.js (v18+) and `pnpm` installed on your machine +- A TMDB API key (free at [themoviedb.org](https://www.themoviedb.org)) ## Nice to know -- [TanStack Router](https://tanstack.com/router/latest/docs/framework/react/routing/routing-concepts) +- [TanStack Router](/router/latest/docs/framework/react/routing/routing-concepts) ## Setting up a TanStack Start Project @@ -48,7 +48,7 @@ pnpm dev At this point, the project structure should look like this: -``` +```text /movie-discovery ├── src/ │ ├── routes/ @@ -75,14 +75,14 @@ To fetch movies from the TMDB API, you need an authentication token. You can get First, let's set up environment variables for our API key. Create a `.env` file in your project root: -``` +```bash touch .env ``` Add your TMDB API token to this file: -``` +```dotenv TMDB_AUTH_TOKEN=your_bearer_token_here ``` @@ -126,23 +126,33 @@ const API_URL = 'https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc' async function fetchPopularMovies(): Promise { - const response = await fetch(API_URL, { - headers: { - accept: 'application/json', - Authorization: `Bearer ${process.env.TMDB_AUTH_TOKEN}`, - }, - }) + + const token = process.env.TMDB_AUTH_TOKEN + if (!token) { + throw new Error('Missing TMDB_AUTH_TOKEN environment variable') + } + + const response = await fetch( + API_URL, + { + headers: { + 'accept': 'application/json', + 'Authorization': `Bearer ${token}`, + }, + } + ) if (!response.ok) { throw new Error(`Failed to fetch movies: ${response.statusText}`) } - return response.json() + const data = (await response.json()) as TMDBResponse + return data } export const Route = createFileRoute('/fetch-movies')({ component: MoviesPage, - loader: async () => { + loader: async (): Promise<{ movies: Movie[]; error: string | null }> => { try { const moviesData = await fetchPopularMovies() return { movies: moviesData.results, error: null } @@ -158,7 +168,7 @@ export const Route = createFileRoute('/fetch-movies')({ Now let's create the components that will display our movie data. Add these components to the same `fetch-movies.tsx` file: -```jsx +```tsx // MovieCard component const MovieCard = ({ movie }: { movie: Movie }) => { @@ -166,7 +176,6 @@ const MovieCard = ({ movie }: { movie: Movie }) => {
{movie.poster_path && ( @@ -208,10 +217,10 @@ const MovieDetails = ({ movie }: { movie: Movie }) => { Finally, let's create the main component that consumes the loader data: -```jsx +```tsx // MoviesPage component const MoviesPage = () => { - const { movies, error } = Route.useLoaderData() + const { movies, error } = Route.useLoaderData()<{ movies: Movie[]; error: string | null }>() return (
{
{error}
@@ -251,7 +259,6 @@ const MoviesPage = () => {
Loading movies...
@@ -267,15 +274,15 @@ const MoviesPage = () => { Let's break down how the different parts of our application work together: -1. Route Loader: When a user visits `/fetch-movies`, the loader function runs on the server -2. API Call: The loader calls `fetchPopularMovies()` which makes an HTTP request to TMDB -3. Server-Side Rendering: The data is fetched on the server reducing the load on the client side -4. Component Rendering: The `MoviesPage` component receives the data via `Route.useLoaderData()` -5. UI Loads: The movie cards are rendered with the fetched data +1. Route loader: When a user visits `/fetch-movies`, the loader function runs on the server +2. API call: The loader calls `fetchPopularMovies()` which makes an HTTP request to TMDB +3. Server-Side rendering: The data is fetched on the server reducing the load on the client side +4. Component rendering: The `MoviesPage` component receives the data via `Route.useLoaderData()` +5. Rendering UI: The movie cards are rendered with the fetched data ## Step 6: Testing Your Application -Now you can test your application by visiting http://localhost:3000/fetch-movies. If everything is set up correctly, you should see a beautiful grid of popular movies with their posters, titles, and ratings. Your app should look like this: +Now you can test your application by visiting [http://localhost:3000/fetch-movies](http://localhost:3000/fetch-movies). If everything is set up correctly, you should see a grid of popular movies with their posters, titles, and ratings. Your app should look like this: ![Netflix style movie setup](https://res.cloudinary.com/dubc3wnbv/image/upload/v1756512946/Screenshot_2025-08-29_at_5.14.26_PM_iiex7o.png) From be35813e2463e552492c61da7bbc7fbf475737cd Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 31 Aug 2025 23:01:06 +0000 Subject: [PATCH 09/11] ci: apply automated fixes --- .../framework/react/fetching-external-api.md | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/docs/start/framework/react/fetching-external-api.md b/docs/start/framework/react/fetching-external-api.md index 2ea2887423e..99279cfdceb 100644 --- a/docs/start/framework/react/fetching-external-api.md +++ b/docs/start/framework/react/fetching-external-api.md @@ -126,21 +126,17 @@ const API_URL = 'https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc' async function fetchPopularMovies(): Promise { - const token = process.env.TMDB_AUTH_TOKEN if (!token) { throw new Error('Missing TMDB_AUTH_TOKEN environment variable') } - const response = await fetch( - API_URL, - { - headers: { - 'accept': 'application/json', - 'Authorization': `Bearer ${token}`, - }, - } - ) + const response = await fetch(API_URL, { + headers: { + accept: 'application/json', + Authorization: `Bearer ${token}`, + }, + }) if (!response.ok) { throw new Error(`Failed to fetch movies: ${response.statusText}`) @@ -169,7 +165,6 @@ export const Route = createFileRoute('/fetch-movies')({ Now let's create the components that will display our movie data. Add these components to the same `fetch-movies.tsx` file: ```tsx - // MovieCard component const MovieCard = ({ movie }: { movie: Movie }) => { return ( @@ -196,9 +191,7 @@ const MovieCard = ({ movie }: { movie: Movie }) => { const MovieDetails = ({ movie }: { movie: Movie }) => { return ( <> -

- {movie.title} -

+

{movie.title}

{movie.overview}

@@ -220,7 +213,10 @@ Finally, let's create the main component that consumes the loader data: ```tsx // MoviesPage component const MoviesPage = () => { - const { movies, error } = Route.useLoaderData()<{ movies: Movie[]; error: string | null }>() + const { movies, error } = Route.useLoaderData()<{ + movies: Movie[] + error: string | null + }>() return (
{
) : ( !error && ( -
+
Loading movies...
) From 310c17d9c20c91228d99d76f47924c7ad6cfcd94 Mon Sep 17 00:00:00 2001 From: shrutikapoor08 Date: Sun, 31 Aug 2025 16:03:50 -0700 Subject: [PATCH 10/11] Adding sequence diagram --- .../framework/react/fetching-external-api.md | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/start/framework/react/fetching-external-api.md b/docs/start/framework/react/fetching-external-api.md index 2ea2887423e..29d6ed522c4 100644 --- a/docs/start/framework/react/fetching-external-api.md +++ b/docs/start/framework/react/fetching-external-api.md @@ -271,6 +271,31 @@ const MoviesPage = () => { ``` ### Understanding How It All Works Together +```mermaid +sequenceDiagram + autonumber + actor U as User + participant R as Router (TanStack Start) + participant L as Route Loader (/fetch-movies) + participant A as External API (TMDB) + participant V as MoviesPage (UI) + + U->>R: Navigate to /fetch-movies + R->>L: Invoke loader (server-side) + L->>A: GET /movie/popular\nAuthorization: Bearer + A-->>L: JSON TMDBResponse + alt response.ok + L-->>R: { movies, error: null } + R->>V: Render SSR with movies + V-->>U: HTML with movie grid + else non-ok / error + L-->>R: { movies: [], error: "Failed to load movies" } + R->>V: Render SSR with error alert + V-->>U: HTML with error state + end + + note over L,V: Loader validates response.ok,\nreturns data or error for initial render +``` Let's break down how the different parts of our application work together: @@ -290,4 +315,4 @@ Now you can test your application by visiting [http://localhost:3000/fetch-movie You've successfully built a movie discovery app that integrates with an external API using TanStack Start. This tutorial demonstrated how to use route loaders for server-side data fetching and building UI components with external data. -While fetching data at build time in TanStack Start is perfect for static content like blog posts or product pages, it's not ideal for interactive apps. If you need features like real-time updates, caching, or infinite scrolling, you'll want to use TanStack Query on the client side instead. TanStack Query makes it easy to handle dynamic data with built-in caching, background updates, and smooth user interactions. By using TanStack Start for static content and TanStack Query for interactive features, you get fast loading pages plus all the modern functionality users expect. +While fetching data at build time in TanStack Start is perfect for static content like blog posts or product pages, it's not ideal for interactive apps. If you need features like real-time updates, caching, or infinite scrolling, you'll want to use [TanStack Query](query/latest) on the client side instead. TanStack Query makes it easy to handle dynamic data with built-in caching, background updates, and smooth user interactions. By using TanStack Start for static content and TanStack Query for interactive features, you get fast loading pages plus all the modern functionality users expect. From 98a3b26090d433ff866543fd64283d0e3ece1b98 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 31 Aug 2025 23:04:55 +0000 Subject: [PATCH 11/11] ci: apply automated fixes --- docs/start/framework/react/fetching-external-api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/start/framework/react/fetching-external-api.md b/docs/start/framework/react/fetching-external-api.md index 814f40cc257..16f49f6ce87 100644 --- a/docs/start/framework/react/fetching-external-api.md +++ b/docs/start/framework/react/fetching-external-api.md @@ -264,6 +264,7 @@ const MoviesPage = () => { ``` ### Understanding How It All Works Together + ```mermaid sequenceDiagram autonumber