> ## Documentation Index
> Fetch the complete documentation index at: https://docs.attio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Navigation

> Send users to places inside Attio from your app

Your app can point the user at three kinds of place inside Attio: one of its own [pages](../pages/overview), a record (optionally on a specific tab, including one of your [record tabs](../extensions/record-tab)), and its own settings page. You describe the place as a **destination**, and Attio decides how to get there.

```js theme={"system"}
import {Destinations, navigate, AttioLink} from "attio/client"
```

Two ways to use a destination:

* [`<AttioLink to={destination}>`](./attio-link) renders a real link. Use it for text the user reads and clicks.
* [`navigate(destination)`](./navigate) goes there from an event handler. Use it after a button click or a completed action.

Both navigate in the current tab, like Attio's own links. There is no confirmation prompt (except when leaving an open dialog, see [`navigate`](./navigate)), and no full page reload.

## Destinations

`Destinations` builds the destination objects that `AttioLink` and `navigate` accept.

<ParamField path="Destinations.appPage(slug)" type="(slug: AppPageSlug) => Destination">
  One of this app's pages, by slug. The slug is the page's folder name under `src/app/pages/`.
</ParamField>

<ParamField path="Destinations.record(record, options?)" type="(record: AttioRecord, options?: {tab?: RecordTab}) => Destination">
  A record's page. `record` is `{recordId, object}`, the same shape record actions and widgets
  receive. Without `tab` the record opens on its overview.

  `tab` accepts one of this app's record tab ids, or the slug of a built-in tab such as
  `"activity"`, `"notes"` or `"tasks"`. If both exist with the same name, your own id wins. A tab
  that doesn't exist on that record, or that the workspace has hidden, falls back to the overview.
</ParamField>

<ParamField path="Destinations.appSettings()" type="() => Destination">
  This app's settings page in workspace settings.
</ParamField>

```tsx theme={"system"}
import {Destinations} from "attio/client"

const record = {recordId: "3ae7c0e6-9f0b-4f6f-9a4e-2d8b1c5f7a10", object: "companies"} // usually from props

Destinations.appPage("dashboard")
Destinations.record(record)
Destinations.record(record, {tab: "enrichment"}) // one of your record tab ids
Destinations.record(record, {tab: "activity"}) // a built-in tab
Destinations.appSettings()
```

## Resolution is optimistic

Attio does not check that a record exists before navigating. A destination for a record id that has been deleted lands the user on Attio's own "record not found" state, the same place a stale bookmark would.

## Failure is a no-op

A destination Attio can't resolve never throws. That covers malformed destinations, a page slug your app doesn't define, and a record destination whose object slug doesn't exist in the workspace.

* `AttioLink` renders its text without a link.
* `navigate` does nothing and logs an error to the browser console so you can see it while developing.

## Typed slugs

`Destinations.appPage(slug)` and the `tab` option are typed against your app's real pages and record tabs, so they autocomplete and typos fail to compile. There is nothing to set up. On every `attio dev` and `attio build`, the CLI writes `.attio/register.d.ts` into your app with the slugs it discovered:

```ts .attio/register.d.ts theme={"system"}
import "attio/client"

declare module "attio/client" {
  export interface Register {
    appPageSlugs: "dashboard" | "sync-log"
    recordTabIds: "enrichment"
  }
}
```

The file is regenerated on every build and deleted when the app has no pages or tabs, so don't edit it or check it in. Until it exists, both parameters accept any string.
