# Configure talizen.config.ts | Creght AI coding guide

> The talizen.config.ts spec for AI: which fields must be static, which may be written as (ctx) => value, plus html/body attributes and head/bodyEnd injected code.

[![Creght](https://ugc.talizen.com/_assets/site/2061660904709165056/1780797461299__creght_logo.png)API for AI](/)

[View llms.txt](/llms.txt)

Overview

- [Creght API for AI](/api.md)

Discoverability

- [How to optimize llms.txt](/api/optimize-llms-txt.md)

Site configuration

- [Configure talizen.config.ts](/api/talizen-config.md)
- [Implement domain-based locale routing](/api/domain-locale-routing.md)

Backend

- [Calling external APIs on the server and managing the cache](/api/ssr-external-api-cache.md)
- [Build site backend workflows with Func](/api/func-backend.md)
- [JSON tables: definition, reads, and queries](/api/func-json-tables.md)
- [Uploads: signed direct upload and Func-generated files](/api/func-assets-upload.md)
- [Timeouts and streaming responses](/api/func-timeout-streaming.md)
- [Integrate Alipay PC Web Payment with Func](/api/func-alipay-payment.md)

Integrations

- [Send Email and Verification Codes with Integrations](/api/func-email-integration.md)
- [Take Alipay payments with an integration](/api/func-alipay-integration.md)

Auth

- [Require a Verified Email to Sign Up](/api/auth-verified-registration.md)
- [Reset and Change Passwords](/api/auth-password-reset.md)
- [Sign In From a Func](/api/auth-func-login.md)
- [Query users from a Func](/api/func-user-directory.md)

On this page

- [What belongs here, and what does not](#what-belongs-here-and-what-does-not)
- [Configuration](#configuration)
- [Endpoint files](#endpoint-files)
- [Page files](#page-files)
- [No layout.tsx](#no-layout-tsx)
- [Fields and when they are evaluated](#fields-and-when-they-are-evaluated)
- [Canonical configuration](#canonical-configuration)
- [html and body: document tag attributes](#html-and-body-document-tag-attributes)
- [head and bodyEnd: injected code](#head-and-bodyend-injected-code)
- [The per-request ctx](#the-per-request-ctx)
- [Metadata layering](#metadata-layering)
- [Implementation checklist](#implementation-checklist)
- [Forbidden implementations](#forbidden-implementations)

Site configuration/Configure talizen.config.ts

# Configure talizen.config.ts

Implementation spec for AI agents: field groups and evaluation timing, html/body tag attributes, head/bodyEnd injected code, and per-request (ctx) => value fields.

Copy Markdown link

Follow this specification when an AI coding agent configures site-level behaviour: dependencies, routing, metadata, the document shell and injected code. `talizen.config.ts` is the single entry point for site-level configuration, and the rules below are implementation constraints, not optional UI suggestions.

**Agent goal**

Produce a valid `talizen.config.ts`: write the fields that only shape the rendered HTML as `(ctx) => value` when they must branch on locale or host, keep build and routing inputs as static values, and never create a `layout.tsx`.

## What belongs here, and what does not

One rule decides it: **anything with its own URL is a file; anything without a URL that applies to every page is configuration.**

### Configuration

Fields in `talizen.config.ts` have no URL of their own. They are the site-level part of every page's HTML.

### Endpoint files

`/robots.ts` → `/robots.txt`, `/sitemap.ts` → `/sitemap.xml`, `/llms.ts` → `/llms.txt`. Each produces one standalone response body, so each is a file.

### Page files

`/pages/about.tsx` → `/about`. A page's own `metadata` / `generateMetadata` applies to that page only.

### No layout.tsx

The platform has no such convention. Creating one does not error — it is silently ignored. Express the document shell with `html` / `body` / `head` / `bodyEnd` below.

## Fields and when they are evaluated

The config file must `export default` a plain object. Do not import packages (except `import type`) and do not use `defineConfig`.

Fields fall into two groups, split by whether the platform needs them _before_ a request exists:

- **Static values only:** `importMap`, `i18n`, `redirects`. Bundling and route building both happen ahead of any request.
- **May be `(ctx) => value`:** `metadata`, `html`, `body`, `head`, `bodyEnd`, `viewport`. These only shape the rendered HTML, so they can be evaluated per request.

> **Hard requirement:** writing `importMap`, `i18n` or `redirects` as a function fails at site load. Those three are read before a request exists, so a function there would be ignored entirely — the page would look fine while the config had no effect. The platform errors instead of degrading silently.

## Canonical configuration

```typescript
import type { TalizenConfig, TalizenConfigContext } from 'talizen'

export default {
  // Static: bundling and route building happen before a request
  importMap: {
    imports: { 'framer-motion': 'https://esm.talizen.com/framer-motion@12' },
  },
  i18n: {
    defaultLocale: 'zh-CN',
    locales: ['zh-CN', 'en'],
  },
  redirects: [
    { source: '/old', destination: '/new', permanent: true },
  ],

  // Document output: a value, or (ctx) => value
  metadata: (ctx: TalizenConfigContext) => ({
    title: {
      template: ctx.locale === 'en' ? '%s | Acme' : '%s ｜ Acme',
      default: 'Acme',
    },
    description: ctx.locale === 'en' ? 'English description' : '中文描述',
  }),

  html: { className: 'dark scroll-smooth' },
  body: { className: 'antialiased bg-neutral-950 text-neutral-100' },

  head: (ctx: TalizenConfigContext) =>
    ctx.host.endsWith('.cn')
      ? '<script async src="https://hm.baidu.com/hm.js?xxx"></script>'
      : '<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXX"></script>',

  bodyEnd: '<script src="/widget.js" defer></script>',
} satisfies TalizenConfig

```

Use `satisfies TalizenConfig` rather than `as`: the former catches mistakes such as a static-only field written as a function while you type, the latter suppresses them.

## html and body: document tag attributes

`html` and `body` are attribute objects applied directly to the server-rendered `<html>` / `<body>`.

- `className` and `class` are equivalent; either works.
- **Do not set `lang`.** The platform fills it from the current locale. Set it only to override that inference.
- Attribute names and values are HTML-escaped.
- This is the correct home for a dark-mode class: attributes declared in config land in the server HTML, so there is no flash from a client-side script.

> **Hard requirement:** shared interactive UI such as a header nav or footer does not belong here. The document shell is a static declaration and cannot hydrate; keep that UI in components that pages import.

## head and bodyEnd: injected code

Both are HTML strings: `head` is injected before `</head>`, `bodyEnd` before `</body>`.

They supersede `customCode.head` / `customCode.body`, and the difference that matters is that they **can be evaluated per request** — for example a different analytics script per domain, which static `customCode` cannot express.

- Injection order is: platform tags → `customCode` → `head` / `bodyEnd`. The newer fields can therefore override the older ones.
- Mark third-party scripts `async` or `defer` yourself; a synchronous script in `head` delays first render.
- Do not repeat tags that `metadata` already expresses ( `title`, `description`, Open Graph) or the page emits two of them.

## The per-request ctx

Fields written as functions are called on every render and receive only these fields:

```typescript
interface TalizenConfigContext {
  locale: string                 // current locale; empty string on single-language sites
  locales?: string[]             // i18n.locales
  defaultLocale?: string         // content baseline locale
  routingDefaultLocale?: string  // the unprefixed default locale of this host
  host: string                   // request host, for per-domain branching
  path: string                   // request path with the locale prefix removed
}

```

> **Hard requirement:** the ctx deliberately offers no cookies and no CMS access. Reading a cookie would make the page cache per cookie, and fetching data at the config layer cannot participate in cache invalidation. **Do not fetch data in config fields**; keep them synchronous and simple. If a function throws, the request fails rather than silently dropping the site-level config.

## Metadata layering

Site-level metadata comes from config; page metadata layers on top.

- A `title.template` in config wraps the literal title a page provides.
- `title.default` is emitted as-is and is **not** wrapped by the template — it means "use this when the page gives no title".
- A page's `title: { absolute: '…' }` bypasses the template.
- To vary site metadata by language, write `metadata` as a function and branch on `ctx.locale`. The `metadata._i18n` field still works but is deprecated; do not use it in new code.

## Implementation checklist

- `export default` a plain object; no package imports except `import type`; no `defineConfig`.
- `importMap`, `i18n` and `redirects` are static values.
- Write a field as a function only when it genuinely branches on `locale` or `host`; otherwise write the value directly.
- `html` / `body` do not set `lang`.
- Third-party scripts carry `async` or `defer`.
- Use `satisfies TalizenConfig` so the types catch mistakes at authoring time.
- After pushing, view source on the real preview URL and confirm the `<html>` / `<body>` attributes, the `<title>` and the position of injected scripts; on multilingual sites verify each locale prefix.

## Forbidden implementations

- Do not create `layout.tsx`: the platform has no such convention and it is silently ignored.
- Do not write `importMap`, `i18n` or `redirects` as functions.
- Do not fetch data in config fields (CMS, fetch, reading cookies).
- Do not hand-write SEO tags in `head` / `bodyEnd` that `metadata` already expresses.
- Do not keep using `customCode` for new work: it is static and cannot branch on locale or host.
- Do not put shared interactive chrome in the document shell; those are components that pages import.
- Do not nest `viewport` inside `metadata`; it is its own top-level field.

![Creght](https://ugc.talizen.com/_assets/site/2061660904709165056/1780797461299__creght_logo.png)

This website is built with [Creght](/)

[Discord](https://discord.gg/Qvq2nmZNnb)

## Links

- [Pricing](/price.md)
- [Solutions](/solution.md)
- [Customers](/customers.md)
- [Help Center](/help.md)
- [Contact Us](/contact.md)
- [Update Logs & Blogs](/blogs.md)
- [Refund Policy](/tuikuan.md)

## Resources

- [All Resources](/resources.md)
- [Templates](/templates.md)
- [Components](https://creghtlib.site.creght.com)
- [Animations](/design/effects.md)
- [Figma to Creght](/figma2creght.md)
- [API for AI](/api.md)

## Terms

- [Terms of Service](/legal/terms.md)
- [Privacy Policy](/legal/privacy.md)
- [Acceptable Use Policy](/legal/acceptable-use.md)

## Social Media

- [Twitter](https://twitter.com)
- [Facebook](https://www.facebook.com)
- [LinkedIn](https://www.linkedin.com)

[蜀ICP备2023038192号-2](https://beian.miit.gov.cn)
