# Convert a Gatsby Site From JavaScript to TypeScript

Yes. An agent converts a Gatsby site's config, pages, templates and components to TypeScript with typed props and query data, builds it and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/convert-javascript-to-typescript/gatsby/), in a Project that has your repository.

## What the agent does

The agent clones the site into its own cloud sandbox and counts what has to change. In a site like gatsbyjs/gatsby-starter-blog there are four config files at the root, `gatsby-config.js`, `gatsby-node.js`, `gatsby-browser.js` and `gatsby-ssr.js`, and under `src` three components (`bio.js`, `layout.js`, `seo.js`), two pages (`404.js`, `index.js`) and one template, `templates/blog-post.js`. One file is already TypeScript: `src/pages/using-typescript.tsx` imports `PageProps`, `HeadFC` and `graphql` from `gatsby` and types its query result with a `DataProps` type, which is the pattern the rest of the site should follow. `package.json` pins `gatsby` `^5.14.6` with React `^18.1.0`, has only `prettier` `^2.8.8` as a dev dependency, uses `package-lock.json`, and its `test` script exits 1 with a reminder to write tests, so the build is the only check.

Gatsby 5 compiles `.ts` and `.tsx` files, and `gatsby-config.ts` and `gatsby-node.ts`, without a plugin, so the conversion needs no bundler change. The agent adds `typescript` as a dev dependency and a `tsconfig.json` with `strict`, `jsx` set for React, `esModuleInterop`, `skipLibCheck` and `noEmit`, plus a `typecheck` script that runs `tsc --noEmit`, because Gatsby's own compile step does not type-check and a pull request that only renames files would prove nothing. `src/pages/using-typescript.tsx` compiles under that config as is, which confirms the settings before anything else moves.

The config files go first because the source files depend on them. `gatsby-config.js` becomes `gatsby-config.ts`: its `module.exports` with a JSDoc `@type {import('gatsby').GatsbyConfig}` becomes an `import type { GatsbyConfig }` and a typed `const config` exported as default, with the `siteMetadata`, the two `gatsby-source-filesystem` entries, the remark plugins and the `gatsby-plugin-feed` serializer typed on its query argument. `gatsby-node.ts` types `createPages` and `onCreateNode` as `GatsbyNode["createPages"]` and `GatsbyNode["onCreateNode"]`, which the JSDoc already named, and gives the `allMarkdownRemark` result a type so `result.data` is no longer `any`. `gatsby-ssr.js` becomes `gatsby-ssr.tsx` with `GatsbySSR["onRenderBody"]`, and `gatsby-browser.js` becomes a `.ts` file of imports.

Then the React files, each renamed to `.tsx`. `layout.js` takes `location`, `title` and `children`, and reads the `__PATH_PREFIX__` global Gatsby injects, so the agent declares that global in a `src/global.d.ts` rather than casting it. `bio.js` and `seo.js` call `useStaticQuery` and get a type for their `site.siteMetadata` shape; `index.js` and `blog-post.js` become `React.FC<PageProps<DataProps>>` with `DataProps` written from each file's `graphql` query, and their `Head` exports become `HeadFC<DataProps>`. The prop that `blog-post.js` destructures as `{ previous, next, site, markdownRemark: post }` gets nullable types, since `gatsby-node.ts` passes `null` for the first and last post. Finally the agent runs `npm run typecheck`, `npm run build` and `npx prettier --check` with the repository's `.prettierrc` (`semi: false`, `arrowParens: avoid`) until all three pass, and opens the pull request.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Convert this Gatsby site from JavaScript to TypeScript. Read package.json, gatsby-config.js, gatsby-node.js, gatsby-browser.js, gatsby-ssr.js, every file under src, and src/pages/using-typescript.tsx, which already shows the typing style to follow. Add typescript as a dev dependency with npm, a strict tsconfig.json with noEmit, and a typecheck script that runs tsc --noEmit. Rename the config files to gatsby-config.ts, gatsby-node.ts, gatsby-ssr.tsx and gatsby-browser.ts, typing them with GatsbyConfig, GatsbyNode and GatsbySSR and replacing module.exports with ES exports. Rename the components, pages and template to .tsx, type page components with PageProps and a DataProps type written from each file's graphql query, type Head exports with HeadFC, give useStaticQuery results a type, and declare the __PATH_PREFIX__ global in a .d.ts file. Do not use any; where a value can be null, say so in the type. Run npm run typecheck, npm run build and prettier --check with the repository's .prettierrc until all pass. Open a pull request that lists every renamed file and each real type error the conversion found.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request on its own branch in which every `.js` file at the root and under `src` has been renamed to `.ts` or `.tsx`, with `tsconfig.json`, `src/global.d.ts`, the `typescript` dev dependency and a `typecheck` script added to `package.json` and `package-lock.json`. The renames are recorded as renames, so the diff shows the typing changes in each file rather than a deletion and an addition. The description lists the files in the order they were converted and quotes the final `tsc --noEmit` and `gatsby build` output from the sandbox.

The description also lists what the compiler found that JavaScript had hidden, with file and line: a prop that can be `null` where the template assumed an object, a `siteMetadata` field read with optional chaining that the query always returns, or a plugin option typed more narrowly than the config passed. Each is either fixed in the same commit or called out as left for review, never silenced with a cast.

Before merging, a reviewer checks that `gatsby build` still generates the same pages, that the `gatsby-plugin-feed` serializer in `gatsby-config.ts` still produces `/rss.xml` with the same fields, and that no `any` or `@ts-ignore` was introduced. The transcript at `GET /o/{org}/sessions/{id}/messages` holds every `tsc` run, including the failing early ones, so the review can follow how each error was resolved.

## Run it on every repository still on JavaScript

A platform team with several Gatsby sites sends this request once per repository, from a script that loops over the list with `resources` set to each one and a `request_id` per repository, so a retried call returns the run already started rather than opening a second pull request. Each run has its own sandbox and its own branch.

Set a webhook URL on the key and Rush POSTs `run.completed` when each turn finishes, with `data.session_url` to read the pull request from. A review comment that asks for a different type on one page becomes a follow-up message on that session, which wakes the parked sandbox with the converted tree still in place.

## Why run it on Rush?

A conversion is a loop of rename, type-check, fix and build, and on a Gatsby site the build step pulls in Sharp image processing and a GraphQL schema build that take a while on a laptop. The agent runs that loop in its own sandbox with its own `node_modules` and `.cache`, and your working tree stays on the branch you were on.

The result is checkable without trusting the description. The pull request carries the exact `tsc --noEmit` and `gatsby build` output, the transcript keeps every intermediate failure, and the rename-aware diff shows only the type annotations that changed. Several sites convert at once, each in its own sandbox.

## Frequently asked questions

### Does the site need a plugin or a bundler change to use TypeScript?

No. Gatsby 5 compiles `.ts` and `.tsx` sources and the `gatsby-config.ts` and `gatsby-node.ts` files itself. The agent adds `typescript` only so `tsc --noEmit` can type-check, because Gatsby's compile step strips types without checking them.

### Will it use any to make the compiler pass?

No. The prompt forbids it, and the places where a JavaScript file was loose get real types: a `DataProps` type per page query, nullable `previous` and `next` props on the post template, and a declared `__PATH_PREFIX__` global. An error the agent cannot type honestly is listed in the pull request instead.

### What about generated GraphQL types?

The conversion writes the query types by hand from each file's `graphql` tag, which keeps the diff small. Gatsby can generate them instead by setting `graphqlTypegen` in `gatsby-config.ts`, which emits a `src/gatsby-types.d.ts`; ask for that in a follow-up and the agent switches the pages to the generated `Queries` namespace.

### Will it change the formatting?

Only in the files it touches, and following the repository's `.prettierrc`, which turns semicolons off and avoids arrow parentheses. The `format` script already matches `.ts` and `.tsx` files, so the existing Prettier setup covers the converted tree without a config change.
