# Rebask cheatsheet

## Start

### create-rebask-app

`--style css` skips Tailwind. See [Getting started](https://www.rebask.com/docs.md#getting-started).

```bash
pnpm create rebask-app
npm create rebask-app@latest
bunx create-rebask-app
yarn create rebask-app
```

### vite.config.ts

See [Configure Vite](https://www.rebask.com/docs.md#manual-setup-_2-configure-vite).

```ts
import rsc from "@vitejs/plugin-rsc"
import react from "@vitejs/plugin-react"
import { rebask } from "@rebask/core/vite"

export default defineConfig({
  plugins: [rsc(), react(), rebask()],
})
```

### vite · vite build · vite preview

Dev, build, serve. See [Add scripts and run](https://www.rebask.com/docs.md#manual-setup-_4-add-scripts-and-run).

## Routing

See [Routing](https://www.rebask.com/docs/routing/introduction.md).

```text
app/
  layout.tsx            wraps below
  page.tsx              /
  about/page.tsx        /about
  blog/[slug]/page.tsx  /blog/:slug
  api/hi/route.ts       /api/hi
```

- `[id]`: `id: string`. See [dynamic segments](https://www.rebask.com/docs/routing/introduction.md#dynamic-segments).
- `[...slug]`: `slug: string[]`. See [dynamic segments](https://www.rebask.com/docs/routing/introduction.md#dynamic-segments).
- `[[...slug]]`: `slug?: string[]`. See [dynamic segments](https://www.rebask.com/docs/routing/introduction.md#dynamic-segments).
- `(group)`: groups files, not part of the URL. See [dynamic segments](https://www.rebask.com/docs/routing/introduction.md#dynamic-segments).

## Project files

- `rebask.config.ts`: runtime config. See [Configuration](https://www.rebask.com/docs/configuration.md).
- `proxy.ts`: runs before any route. See [Proxy](https://www.rebask.com/docs/routing/proxy.md).
- `instrumentation.ts`: `register()` runs on boot. See [Telemetry](https://www.rebask.com/docs/telemetry.md).
- `rebask.image.ts`: image optimizer. See [Image plugin setup](https://www.rebask.com/docs/images.md#image-plugin-setup).
- `public/`: static files. See [Static files](https://www.rebask.com/docs/routing/proxy.md#static-files).

## Pages and layouts (server)

- `page.tsx`: default export, receives no props. See [Pages and layouts](https://www.rebask.com/docs/routing/introduction.md#pages-and-layouts).
- `layout.tsx`: receives `children`. The root one renders `<html>`. See [Pages and layouts](https://www.rebask.com/docs/routing/introduction.md#pages-and-layouts).
- `prerenderParams()`: `allowExtraParams: false` 404s other paths. See [`prerenderParams`](https://www.rebask.com/docs/routing/introduction.md#prerenderparams).
- `markdown.md` / `markdown.ts`: Markdown for `Accept: text/markdown` and the route's `.md` URL. See [Agents](https://www.rebask.com/docs/agents.md#markdown).
- `export const notFound = true`: on a catch-all page, the 404 for unmatched URLs. See [Unmatched URLs](https://www.rebask.com/docs/error-handling.md#unmatched-urls).

```ts
export function prerenderParams() {
  return {
    params: [{ id: "1" }, { id: "2" }],
    allowExtraParams: false,
  }
}
```

## Route handlers (server)

`route.ts` exports HTTP methods. See [Methods and signature](https://www.rebask.com/docs/routing/route-handlers.md#methods-and-signature).

```ts
export async function GET(
  req: Request,
  ctx: { params: { id: string } },
) {
  return Response.json(ctx.params)
}
```

- `GET POST PUT PATCH DELETE`: named exports. See [Methods and signature](https://www.rebask.com/docs/routing/route-handlers.md#methods-and-signature).
- `HEAD OPTIONS`: automatic. Missing methods get a `405`. See [Automatic methods and errors](https://www.rebask.com/docs/routing/route-handlers.md#automatic-methods-and-errors).
- `Cache-Control`: responses are only cached if you send it. See [Caching and invalidation](https://www.rebask.com/docs/routing/route-handlers.md#caching-and-invalidation).

## Directives

- `"use client"`: state, effects, and events. Server is the default. See [Components](https://www.rebask.com/docs/rendering/components.md).
- `"use server"`: exports become functions the client can call. See [Defining Server Functions](https://www.rebask.com/docs/server-functions.md#defining-server-functions).
- `"use cache"`: caches the result. Stale after an hour, then refreshed in the background. See [`"use cache"`](https://www.rebask.com/docs/caching/server-components.md#use-cache).

## Request (server)

Paths: `@rebask/core/request`, `@rebask/core/cookies`.

`headers() searchParam() routeParams()` are not available in `"use cache"`. See [Server Runtime APIs](https://www.rebask.com/docs/server-runtime-apis.md).

```ts
const h = await headers()
const q = await searchParam("q")
const { id } = await routeParams<P>()
```

- `searchParam()`: `null`, a string, or an array when repeated. See [`searchParam()`](https://www.rebask.com/docs/server-runtime-apis.md#searchparamname).
- `routeParams()`: read under `<Suspense>` to keep the rest in the shell. See [`routeParams()`](https://www.rebask.com/docs/server-runtime-apis.md#routeparams).
- `cookies(request) cookies(response)`: for route handlers and the proxy. The request is read only. See [Cookies](https://www.rebask.com/docs/server-runtime-apis.md#cookies).

```ts
const store = await cookies()
store.get("id")
store.set("id", id, { maxAge: 3600 })
store.delete("id")
```

## Server Functions

- `<form action>`: works without JavaScript. Receives `FormData`. See [Server Functions with form actions](https://www.rebask.com/docs/server-functions.md#server-functions-with-form-actions).
- `useActionState` (`client`): the action gets `(prevState, formData)`. See [Server Functions with form actions](https://www.rebask.com/docs/server-functions.md#server-functions-with-form-actions).
- `.bind(null, id)`: bound args first, `FormData` last. See [Arguments](https://www.rebask.com/docs/server-functions.md#arguments).
- Inline "use server": define it in a Server Component, pass it as a prop. See [Defining Server Functions](https://www.rebask.com/docs/server-functions.md#defining-server-functions).
- After a mutation: client data isn't refreshed on its own. See [Mutations](https://www.rebask.com/docs/server-functions.md#mutations).

```tsx
<form action={createPost}>
  <input name="title" />
</form>
```

```tsx
const [state, action, pending] =
  useActionState(save, { error: null })
```

```ts
await revalidateTag("posts") // server
await router.revalidate()    // client
```

## Navigations

Paths: `@rebask/core/components/link`, `@rebask/core/router`.

`<Link>` example. See [`<Link>`](https://www.rebask.com/docs/routing/navigations.md#link).

```tsx
<Link href="/a">A</Link>
<Link href="/b" prefetch="none" />
<Link href="/c" scroll={false} />
```

- `prefetch="intent"`: default. Shell in view, full on hover. Like `viewport` on mobile. See [`<Link>`](https://www.rebask.com/docs/routing/navigations.md#link).
- `prefetch="viewport"`: full payload when visible. See [`<Link>`](https://www.rebask.com/docs/routing/navigations.md#link).
- `prefetch="none"`: shell in view, full on click. Nothing prefetches in dev. See [`<Link>`](https://www.rebask.com/docs/routing/navigations.md#link).
- `<meta httpEquiv="refresh">` (`server`): redirect after a data check. Full document navigation. See [Redirects](https://www.rebask.com/docs/routing/navigations.md#redirects).
- `router.replace()` (`client`): redirect without a full reload or history entry. See [`router.replace()`](https://www.rebask.com/docs/routing/navigations.md#userouter-routerreplace).
- `history.pushState() history.replaceState()` (`client`): update the URL and hooks without navigating.
- `useRouter()` (`client`). See [`useRouter()`](https://www.rebask.com/docs/routing/navigations.md#userouter).
- `usePathname() useParams()` (`client`): read under `<Suspense>`. See [`usePathname()`](https://www.rebask.com/docs/routing/navigations.md#usepathname).
- `<Suspense>`: there is no implicit boundary. On a soft navigation, the nearest boundary catches the loading page segment, and a root-only fallback replaces everything it wraps. Wrap the dynamic parts of the page with `Suspense`, and wrap `children` in the closest layout to catch the page segment. See [Suspense and navigations](https://www.rebask.com/docs/routing/navigations.md#suspense-and-navigations).

```ts
const router = useRouter()
router.push("/a", { scroll: false })
router.replace("/login")
await router.prefetch("/checkout")
```

```tsx
if (!post) return (
  <meta httpEquiv="refresh" content="0;url=/blog" />
)
```

## Client cache

Paths: `@rebask/core/router`, `@rebask/core/components/cache-hints`, `@rebask/core/components/revalidate-segment`, `@rebask/core/components/route-hints`.

- `router.revalidate()` (`client`): refetch while showing stale data. See [`router.revalidate()`](https://www.rebask.com/docs/routing/navigations.md#userouter-routerrevalidate).
- `<CacheHints>` (`server`): seconds this browser keeps it fresh, minimum 2. `mustRevalidate` disables SWR. See [`<CacheHints>`](https://www.rebask.com/docs/routing/navigations.md#cachehints).
- `router.revalidateTag()` (`client`): refetch by client tag. `router.setCacheHints` sets them. See [`router.revalidateTag()`](https://www.rebask.com/docs/routing/navigations.md#userouter-routerrevalidatetag).
- `<RevalidateSegment />`: refetch on focus and reconnect. `interval` polls. See [`<RevalidateSegment />`](https://www.rebask.com/docs/routing/navigations.md#revalidatesegment).
- `<RouteHints prediction={false} />` (`server`): stop reusing `/blog/[slug]` for `/blog/2`. See [Route prediction](https://www.rebask.com/docs/routing/navigations.md#route-prediction).

```ts
await router.revalidate()
await router.revalidate("/posts", {
  evict: true, // show the fallback
  immediate: true, // even if hidden
})
```

```tsx
<CacheHints stale={60} tags={["a"]} />
```

## Server cache (server)

Path: `@rebask/core/cache`.

`"use cache"` caches function results. See [`"use cache"`](https://www.rebask.com/docs/caching/server-components.md#use-cache).

```ts
async function getPost(id: string) {
  "use cache"
  cacheLife({ revalidate: 60 })
  cacheTag("posts", `post:${id}`)
  return db.posts.find(id)
}
```

- `cacheLife({ revalidate, expire })`: seconds, shortest wins. Stale after `revalidate`, gone after `expire` (a year). In a page, sets the page lifetime. See [`cacheLife`](https://www.rebask.com/docs/caching/cache-lifetimes.md#cachelife).
- `cacheTag(...tags)`: tags also flow up to the page. See [`cacheTag`](https://www.rebask.com/docs/caching/cache-lifetimes.md#cachetag).
- `revalidateTag(tag)`: expires every server entry with the tag. See [`revalidateTag`](https://www.rebask.com/docs/caching/cache-lifetimes.md#revalidatetag).
- `durableCache(fn, deps)`: survives deploys. JSON only, list other inputs in `deps`. See [`durableCache`](https://www.rebask.com/docs/caching/server-components.md#durablecache).
- `fetch()`: identical `GET`s run once per request. A `signal` opts out. See [Fetching in Server Components](https://www.rebask.com/docs/caching/server-components.md#fetching-in-server-components).

```ts
const getRates = durableCache(
  async (id: string) => getRate(id),
  [API_URL],
)
```

## Errors

Paths: `@rebask/core/components/errors`, `@rebask/core/components/error-boundary`.

- `<NotFound>` (`server`): return it when data is missing. Sets a 404. See [`<NotFound>`](https://www.rebask.com/docs/error-handling.md#notfound).
- `<ServerError status={503}>` (`server`): your error UI with an error status. Not cached. Handle errors with `try/catch` and return it from `catch`. See [`<ServerError>`](https://www.rebask.com/docs/error-handling.md#servererror).
- `<ErrorBoundary fallback>`: client component. Catches server errors too, but the fallback only renders in the browser. See [`<ErrorBoundary>`](https://www.rebask.com/docs/error-handling.md#errorboundary).

```tsx
if (!post) {
  return <NotFound>Gone</NotFound>
}
```

## Metadata

Path: `@rebask/core/components/head`.

- `<title> <meta>`: render them anywhere. There is no `metadata` export. See [Document tags](https://www.rebask.com/docs/metadata.md#document-tags).
- `<Head>`: dedupes across layouts. See [`<Head>`](https://www.rebask.com/docs/metadata.md#head).

## Images

Paths: `@rebask/core/vite/image`, `@rebask/core/image`, `@rebask/core/components/image`.

`rebaskImage()` enables image optimization. See [Image plugin setup](https://www.rebask.com/docs/images.md#image-plugin-setup).

```ts
// vite.config.ts
plugins: [/* … */, rebaskImage()]

// rebask.image.ts
export default (req: Request) =>
  handleImage(req, sharp)
```

`<Image>` also supports `placeholder="blur"`, `quality`, and `loader`. See [`<Image>`](https://www.rebask.com/docs/images.md#image).

```tsx
<Image src="/a.jpg" alt="" preload
  width={800} height={600} />
<Image src={url} alt="" fill />
```

## Proxy (server)

Path: `@rebask/core/proxy`.

`proxy.ts` returns a `Response` to end, nothing to continue. See [Proxy](https://www.rebask.com/docs/routing/proxy.md).

```ts
export default {
  "/admin/*": (req) => {
    if (!cookies(req).has("sid"))
      return Response.redirect(login)
  },
  "/old/:slug": (_, { slug }) =>
    rewrite(`/blog/${slug}`),
} satisfies ProxyRoutes
```

- `:slug`, `*`, `**`, groups, and regex constraints use rou3 patterns. See [Route patterns](https://www.rebask.com/docs/routing/proxy.md#route-patterns).
- `rewrite("https://…")`: proxies to another origin. `502` on failure. See [External rewrites](https://www.rebask.com/docs/routing/proxy.md#external-rewrites).

## Config

Paths: `@rebask/core/vite`, `@rebask/core`.

`rebask()` defaults. See [`rebask()`](https://www.rebask.com/docs/configuration.md#rebask).

```ts
rebask({
  prerender: true,
  dedupeFetch: true,
  fullHtmlForBots: false,
  markdownUrls: true,
  proxyTimeout: 30_000,
  shellPrerenderTimeout: 30_000,
  clientTraceMetadata: [],
})
```

`rebask.config.ts` configures the runtime. See [`rebask.config.ts`](https://www.rebask.com/docs/configuration.md#rebaskconfigts).

```ts
export default {
  cacheHandler: new RedisCache(),
  serverFunctions: {
    allowedOrigins: ["*.example.com"],
    bodySizeLimit: 1024 * 1024,
  },
} satisfies RebaskConfig
```
