A project holds the text for one product surface, usually one Figma file or one feature. Text inside a project is a set of text items. A text item is one string plus its metadata: status, tags, notes, assignee, character limit, variants, plurals, and the Figma layers it is linked to. Editing a text item updates every linked layer.
Blocks group text items within a project, in display order. They are how a project’s text is organized on screen; the API and MCP address them by developer ID.
The component library is one per workspace. A library component is reusable text: attach it to text items in any number of projects and editing the component updates every instance. With component versioning, instances update when they’re upgraded instead. Components live in folders, which can nest. A scan creates library components, in folders, from the strings in your codebase.
Text items and library components share the same developer ID space; a component and its attached instances share one developer ID, except with component versioning, where each instance has its own.
Component versioning (called library component versioning in the API reference) lets you change a library component without changing its instances right away. Each published change gives the component a new version, and each instance stays on its version until it’s upgraded. A workspace admin or editor turns on versioning from the component library in the web app. Turning it on can’t be undone. More detail: Component versioning.In a workspace with component versioning:
Publishing.Publish component changes turns a component’s unpublished changes into a new version. Update components creates a new published version, but can’t change a component’s content while it has existing unpublished changes. Changing only status or assignee can happen independently of publishing constraints.
Instances don’t change when their component gets a new version. Each instance keeps its own status, assignee, and integrated flag. An instance has local edits when its text was changed on the instance rather than on the component. Local edits occur in a limited capacity through indirect means like figma canvas updates. Fetch text items reports where each instance stands in component.state:
State
Meaning
current
On the component’s latest version, with no local edits
outdated
The component has a newer version; no local edits
modified
On the component’s latest version, with local edits
conflict
Has local edits, and the component has a newer version
Upgrading moves an instance to its component’s latest version, with that version’s text. Upgrade instances from the web app or with Upgrade component instances. Upgrading replaces local edits, so the API refuses to upgrade a modified or conflict instance unless you set discardLocalEdits.
Developer IDs. Each instance has its own developer ID instead of sharing its component’s, so it can be referenced in code separately from the component. A text item attached to a component keeps the developer ID it had, and publishing a text item to the library gives the new component a newly generated one.
Text items fetched from a project hold each instance’s own text, so a component’s new version reaches that code only once its instances are upgraded. Components fetched from the library hold each component’s latest published version.The versioning endpoints return a 404 in workspaces that haven’t turned on component versioning.
Developer IDs are unique, usually human-readable identifiers for projects, text items, library components, component folders, blocks, variables, variants, and style guides. They are the key you reference in code and the key you pass to the API, CLI, and MCP tools.Developer IDs are generated when an item is created: projects, components, and folders from their name, text items from their text. To keep references stable, Ditto never changes a developer ID automatically when the underlying data changes. Editing a text item’s text does not change its developer ID.You can edit a developer ID by hand at any time.
If you change a developer ID in Ditto, update every reference to it in your application. Make such edits before an
item is in use in code.
Developer IDs for a given entity type are unique within a workspace. Two projects cannot share an ID, and two pieces of text cannot share an ID, but a project and a text item may.Text items and library components draw from the same pool: a library component and an unrelated text item cannot share a developer ID, and similar text items in different projects cannot either, with one exception: a library component and the text items attached to it all share the component’s developer ID, because their content is kept identical.Example. A library component has developer ID hello. Two projects each get a text item with the text “hello”; they are assigned hello-1 and hello-2. Attaching both to the component gives all three the developer ID hello.With component versioning, the exception doesn’t apply: each instance keeps its own developer ID. In the example, the two text items stay hello-1 and hello-2 after they’re attached.
Developer IDs are generated and validated against a workspace-wide Developer ID configuration, editable on the Developers page:
Allowed characters: the set of characters permitted in a developer ID.
Character replacement: an ordered list of substring replacements applied to the source text during generation.
Casing adjustment: optionally transform the case of the source text. Options: lowercase, UPPERCASE, camelCase, PascalCase.
Maximum length: the maximum number of characters. Deduplication may append a short suffix after truncation, so generated IDs can exceed the limit by a few characters. If your system has a hard limit, set the maximum three or more characters below it. Manual edits over the limit are rejected.
Text items and library components can be marked integrated to signal that their developer ID is in use in code: changing the ID would be a breaking change, and changing the text changes production copy.The flag is a filter on every API endpoint that fetches text, on the CLI config (integrated: true), and on every webhook payload (integrated: boolean), so you can limit what reaches your codebase to text that is known to be in use.
Projects: open the project’s main menu (top right of the project in the web app) and choose Development integration.Text items and library components: select the item and edit the Developer ID field in the details panel.Select several items to open the Bulk editor, which edits many IDs at once and supports operations such as adding a prefix. Details: Developer ID bulk editor.Library folders: open the folder’s parent, choose Folder properties… from the folder’s menu, and edit the Developer ID in the details panel.Variants: open Variants, select a variant, and edit the Developer ID in the details panel.
A style guide is a named ruleset for how your product’s text reads. A workspace can have several. Each has a description and an enabled by default flag; enabled-by-default guides apply to every project unless a project turns them off, and to the component library, which has no per-guide setting of its own.
A style guide is divided into sections, each of kind rules or wordlist.
A rule (in a rules section) has a name, a description of what to do, one or more examples as from → to pairs, optional tags that limit it to text carrying those tags, and an enabled flag.
A word list entry (in a wordlist section) has a preferred term, disallowed alternatives, and a description.
A style guide can be scoped to a variant with a locale code, which is how locale-specific rules (formality, terminology) are attached to a language.
Agents fetch rules with get_styleguide_rules and check text with suggest_edit; the PR review bot enforces them on pull requests; the Ditto app applies them through Magic Edit and Magic Translate. After a scan, Ditto drafts a starter style guide from your strings; see Get a first style guide from your scan.
A variant is a named alternative for a text item’s text. Every text item and library component has base text and any number of variant texts. Variants are workspace-level objects with a name, description, developer ID, and optional locale code.A locale is simply a variant with a locale code (de-DE, fr, es-419). Ditto’s localization features key on it: Magic Translate uses it as the target language, locale-scoped style guides attach to it, and the CLI’s iosLocales and androidLocales map it to platform locale directories. At most one variant per workspace holds a given locale code.Variants without a locale code cover other uses, such as tone or A/B copy.The base locale is the language of your base text, set in a project’s or the library’s Variant & locale settings. Set it when your source copy is not English.How to add locales, get translations, and pull localized files: Set up localization.
Variables are reusable placeholders for values your app fills in at runtime. Write one into text as {{variable_name}}. Each variable has a type: string or number (with an example and optional fallback), hyperlink (link text and URL), list (a set of possible values), or map (key-value pairs).A variable’s name is its developer ID: letters, numbers, and underscores, unique in the workspace.The API, CLI, and manual exports render variables in each string format’s native placeholder syntax (for example {username} in ICU JSON, %1$@ in iOS .strings, <xliff:g> in Android XML). A scan detects the variables in your strings and creates them. More detail: Variables.
A text item can define plural forms (zero, one, two, few, many, other) so your app renders different copy for different counts: “No results”, “1 result”, “10 results”.The API returns the base form (pluralForm: null) and each defined plural form as separate objects. Following i18next conventions, each plural form’s developer ID is the text item’s developer ID with a suffix, for example results_one. In your code you reference the main developer ID and pass a count; the i18n library picks the form.Plurals can contain variables and can be translated per variant. Each string format renders plurals natively (.stringsdict on iOS, <plurals> on Android, {count, plural, …} in ICU). More detail: Plurals.
Tags are labels on text items and library components. Style guide rules can be limited to tagged text, and the API, CLI, and search_ditto_text filter by tag. Only tags that already exist in the workspace can be used on a rule; get_workspace_tags lists them. A scan proposes a tag vocabulary for your strings.
Statuses track text through your workflow, from first draft to ready to ship. The default set is NONE, WIP, REVIEW, FINAL, but a workspace can rename or replace it, so call list_statuses (MCP) or GET /v2/statuses before setting or filtering by status. Filtering pull to FINAL keeps unreviewed text out of production.
npx @dittowords/cli scan <path> extracts candidate strings from your code and uploads them for review. A candidate is one string with where it was found, how it was found (markup text, an attribute value, a value in a localization resource file, or another string position), its translation key and locale if it came from a localization file, and a few surrounding lines of source.Ditto then:
Classifies each candidate as user-facing or not.
Proposes a content system for the user-facing strings: a tag vocabulary, a naming scheme for components, a folder structure, the variants and locales found in your translation files, and a developer ID configuration derived from your existing keys. You review and adjust this before anything is created.
Creates the library components (in folders, with tags), the variables they use, and one variant per locale.
Drafts a style guide from a sample of the strings, for you to review and enable.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Ditto","description":"Developer documentation for Ditto, the source of truth for product text. Scan a codebase to import its strings, then connect coding agents through the Ditto MCP server, link Figma files, and pull localized string files with the CLI.","colors":{"primary":"#E2B103","light":"#F0D164","dark":"#1D1700"},"logo":{"light":"https://mintcdn.com/ditto/A9jN5zv6WG_Dk9hK/logo/ditto_logo.svg?fit=max\u0026auto=format\u0026n=A9jN5zv6WG_Dk9hK\u0026q=85\u0026s=4e38ddaa8955775a191cbe1616e02312","dark":"https://mintcdn.com/ditto/A9jN5zv6WG_Dk9hK/logo/ditto_logo.svg?fit=max\u0026auto=format\u0026n=A9jN5zv6WG_Dk9hK\u0026q=85\u0026s=4e38ddaa8955775a191cbe1616e02312"},"favicon":"https://mintcdn.com/ditto/WgRtoF9rLXWv6hNh/favicon.jpeg?fit=max\u0026auto=format\u0026n=WgRtoF9rLXWv6hNh\u0026q=85\u0026s=011fcbfc2411f0c04ab811e7b701750c","background":{"color":{"dark":"#11141B"}},"navbar":{"links":[{"href":"mailto:support@dittowords.com","label":"Support"}],"primary":{"type":"button","label":"Open App","href":"https://app.dittowords.com"}},"footer":{"socials":{"twitter":"https://twitter.com/dittowords","github":"https://github.com/dittowords","linkedin":"https://www.linkedin.com/company/ditto-app"}},"seo":{"organization":{"id":"https://www.dittowords.com/#organization","name":"Ditto","url":"https://www.dittowords.com","logo":"https://developer.dittowords.com/logo/ditto_logo.svg","sameAs":["https://twitter.com/dittowords","https://github.com/dittowords","https://www.linkedin.com/company/ditto-app"]},"indexing":"navigable"},"contextual":{"options":["copy","view","claude","chatgpt","mcp","cursor","vscode"],"display":"header"},"markdown":{"instructions":["These are Ditto's developer docs. Ditto is a single source of truth for product copy across design, review, localization, and code.","The first step on any codebase is a scan: https://developer.dittowords.com/get-started/scan-your-repo.md. That page also covers reviewing the style guide Ditto drafts from the scan. After it, the follow-on guides are Connect Figma files and Set up localization (under /guides/) and Connect the Ditto MCP (the Setup page of the MCP reference).","Two different MCP servers: the Ditto MCP server at https://api.dittowords.com/v2/mcp gives an agent a workspace's text and style guide rules; the documentation MCP server at https://developer.dittowords.com/mcp searches these docs.","Prefer logging in with a Ditto account (npx -y @dittowords/cli@latest login; OAuth in MCP clients) over API keys. API keys are for CI and headless clients only.","Commands are complete as written and use npx -y @dittowords/cli@latest; nothing needs a global install."]},"styling":{"latex":false},"navigation":{"global":{"anchors":[{"anchor":"Help Guides","icon":"book-open-cover","href":"https://help.dittowords.com/en/"},{"anchor":"What's New","icon":"newspaper","href":"https://www.dittowords.com/whats-new"},{"anchor":"Figma Plugin","icon":"figma","href":"https://www.figma.com/community/plugin/798826066406007173/ditto-manage-copy-from-design-to-production"}]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"ditto","hasAudioNarration":false,"actualSubdomain":"ditto","customDomains":["developer.dittowords.com"],"gitSource":{"type":"github","owner":"dittowords","repo":"ditto-app","deployBranch":"master","contentDirectory":"docs-public","isPrivate":false,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"REMOVE_BRANDING":{"status":"DISABLED"},"CONTEXTUAL_FEEDBACK":{"status":"DISABLED"},"CODE_SNIPPET_FEEDBACK":{"status":"DISABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6aee3ddcc5d9abc893e67d94","clientVersion":"0.0.4087","searchFilterCounts":[{"language":"default","tag":"Documentation","count":97},{"language":"default","tag":"API Reference","count":68},{"language":"default","tag":"Endpoints","count":66},{"language":"default","tag":"Legacy","count":18},{"language":"default","tag":"Components","count":16},{"language":"default","tag":"Projects","count":8},{"language":"default","tag":"Text Items","count":8},{"language":"default","tag":"CLI Reference","count":7},{"language":"default","tag":"Component Folders","count":6},{"language":"default","tag":"Style guides","count":6}]},"assistantConfig":{},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"concepts","slug":"/concepts","pageMetadata":{"title":"Key concepts","description":"Definitions for Ditto's data model: projects, text items, library components, component versioning, developer IDs, style guides, variants, variables, plurals, and scans.","keywords":["text items","library components","component versioning","developer IDs","variants","variables","plurals","style guides","blocks"],"href":"/concepts"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Definitions for Ditto’s data model: projects, text items, library components, component versioning, developer IDs, style guides, variants, variables, plurals, and scans.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,em:`em`,li:`li`,ol:`ol`,p:`p`,strong:`strong`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{Heading,OptimizedImage,Table,Warning}=_components;if(!Heading)_missingMdxReference(`Heading`,true);if(!OptimizedImage)_missingMdxReference(`OptimizedImage`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(_components.p,{children:`This page defines the terms the CLI, API, MCP tools, and guides use.`}),`\n`,_jsx(Heading,{level:`2`,id:`projects-text-items-and-the-component-library`,children:`Projects, text items, and the component library`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`A `,_jsx(_components.strong,{children:`project`}),` holds the text for one product surface, usually one Figma file or one feature. Text inside a project is a set of `,_jsx(_components.strong,{children:`text items`}),`. A text item is one string plus its metadata: status, tags, notes, assignee, character limit, variants, plurals, and the Figma layers it is linked to. Editing a text item updates every linked layer.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Blocks`}),` group text items within a project, in display order. They are how a project’s text is organized on screen; the API and MCP address them by developer ID.`]}),`\n`,_jsxs(_components.li,{children:[`The `,_jsx(_components.strong,{children:`component library`}),` is one per workspace. A `,_jsx(_components.strong,{children:`library component`}),` is reusable text: attach it to text items in any number of projects and editing the component updates every instance. With `,_jsx(_components.a,{href:`#component-versioning`,children:`component versioning`}),`, instances update when they’re upgraded instead. Components live in `,_jsx(_components.strong,{children:`folders`}),`, which can nest. A `,_jsx(_components.a,{href:`/get-started/scan-your-repo`,children:`scan`}),` creates library components, in folders, from the strings in your codebase.`]}),`\n`,_jsxs(_components.li,{children:[`Text items and library components share the same developer ID space; a component and its attached instances share one developer ID, except with `,_jsx(_components.a,{href:`#component-versioning`,children:`component versioning`}),`, where each instance has its own.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`For designers’ view of these concepts, see `,_jsx(_components.a,{href:`https://help.dittowords.com/en/articles/11502480-text-items-and-linking`,children:`Text items and linking`}),` and `,_jsx(_components.a,{href:`https://help.dittowords.com/en/articles/11502584-component-library`,children:`Component library`}),` in the help center.`]}),`\n`,_jsx(Heading,{level:`2`,id:`component-versioning`,children:`Component versioning`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Component versioning`}),` (called library component versioning in the API reference) lets you change a library component without changing its instances right away. Each published change gives the component a new `,_jsx(_components.strong,{children:`version`}),`, and each instance stays on its version until it’s `,_jsx(_components.strong,{children:`upgraded`}),`. A workspace admin or editor turns on versioning from the component library in the web app. Turning it on can’t be undone. More detail: `,_jsx(_components.a,{href:`https://help.dittowords.com/en/articles/17663939-component-versioning`,children:`Component versioning`}),`.`]}),`\n`,_jsx(_components.p,{children:`In a workspace with component versioning:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Unpublished changes.`}),` A component’s text, plurals, tags, notes, and character limit can be changed without publishing, using `,_jsx(_components.a,{href:`/api-reference/components/save-unpublished-component-changes`,children:`Save unpublished component changes`}),` in the API. Instances don’t see those changes. `,_jsx(_components.a,{href:`/api-reference/components/get-components`,children:`Fetch components`}),` still returns the published component, with `,_jsx(_components.code,{children:`hasUnpublishedChanges`}),` set, and `,_jsx(_components.a,{href:`/api-reference/components/get-unpublished-component-changes`,children:`Fetch unpublished component changes`}),` returns it with the changes applied. `,_jsx(_components.a,{href:`/api-reference/components/discard-unpublished-component-changes`,children:`Discard unpublished component changes`}),` throws them away.`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Publishing.`}),` `,_jsx(_components.a,{href:`/api-reference/components/publish-component-changes`,children:`Publish component changes`}),` turns a component’s unpublished changes into a new version. `,_jsx(_components.a,{href:`/api-reference/components/patch-components`,children:`Update components`}),` creates a new published version, but can’t change a component’s content while it has existing unpublished changes. Changing only status or assignee can happen independently of publishing constraints.`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Instances`}),` don’t change when their component gets a new version. Each instance keeps its own status, assignee, and integrated flag. An instance has `,_jsx(_components.strong,{children:`local edits`}),` when its text was changed on the instance rather than on the component. Local edits occur in a limited capacity through indirect means like figma canvas updates. `,_jsx(_components.a,{href:`/api-reference/text-items/get-text-items`,children:`Fetch text items`}),` reports where each instance stands in `,_jsx(_components.code,{children:`component.state`}),`:`]}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`State`}),_jsx(_components.th,{children:`Meaning`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`current`})}),_jsx(_components.td,{children:`On the component’s latest version, with no local edits`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`outdated`})}),_jsx(_components.td,{children:`The component has a newer version; no local edits`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`modified`})}),_jsx(_components.td,{children:`On the component’s latest version, with local edits`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`conflict`})}),_jsx(_components.td,{children:`Has local edits, and the component has a newer version`})]})]})]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Upgrading`}),` moves an instance to its component’s latest version, with that version’s text. Upgrade instances from the web app or with `,_jsx(_components.a,{href:`/api-reference/components/upgrade-component-instances`,children:`Upgrade component instances`}),`. Upgrading replaces local edits, so the API refuses to upgrade a `,_jsx(_components.code,{children:`modified`}),` or `,_jsx(_components.code,{children:`conflict`}),` instance unless you set `,_jsx(_components.code,{children:`discardLocalEdits`}),`.`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Developer IDs.`}),` Each instance has its own developer ID instead of sharing its component’s, so it can be referenced in code separately from the component. A text item attached to a component keeps the developer ID it had, and publishing a text item to the library gives the new component a newly generated one.`]}),`\n`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`Text items fetched from a project hold each instance’s own text, so a component’s new version reaches that code only once its instances are upgraded. Components fetched from the library hold each component’s latest published version.`}),`\n`,_jsxs(_components.p,{children:[`The versioning endpoints return a `,_jsx(_components.code,{children:`404`}),` in workspaces that haven’t turned on component versioning.`]}),`\n`,_jsx(Heading,{level:`2`,id:`developer-ids`,children:`Developer IDs`}),`\n`,_jsx(_components.p,{children:`Developer IDs are unique, usually human-readable identifiers for projects, text items, library components, component folders, blocks, variables, variants, and style guides. They are the key you reference in code and the key you pass to the API, CLI, and MCP tools.`}),`\n`,_jsx(_components.p,{children:`Developer IDs are generated when an item is created: projects, components, and folders from their name, text items from their text. To keep references stable, Ditto never changes a developer ID automatically when the underlying data changes. Editing a text item’s text does not change its developer ID.`}),`\n`,_jsx(_components.p,{children:`You can edit a developer ID by hand at any time.`}),`\n`,_jsx(Warning,{children:_jsx(_components.p,{children:`If you change a developer ID in Ditto, update every reference to it in your application. Make such edits before an\nitem is in use in code.`})}),`\n`,_jsx(Heading,{level:`3`,id:`uniqueness`,children:`Uniqueness`}),`\n`,_jsx(_components.p,{children:`Developer IDs for a given entity type are unique within a workspace. Two projects cannot share an ID, and two pieces of text cannot share an ID, but a project and a text item may.`}),`\n`,_jsx(_components.p,{children:`Text items and library components draw from the same pool: a library component and an unrelated text item cannot share a developer ID, and similar text items in different projects cannot either, with one exception: a library component and the text items attached to it all share the component’s developer ID, because their content is kept identical.`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Example.`}),` A library component has developer ID `,_jsx(_components.code,{children:`hello`}),`. Two projects each get a text item with the text “hello”; they are assigned `,_jsx(_components.code,{children:`hello-1`}),` and `,_jsx(_components.code,{children:`hello-2`}),`. Attaching both to the component gives all three the developer ID `,_jsx(_components.code,{children:`hello`}),`.`]}),`\n`,_jsxs(_components.p,{children:[`With `,_jsx(_components.a,{href:`#component-versioning`,children:`component versioning`}),`, the exception doesn’t apply: each instance keeps its own developer ID. In the example, the two text items stay `,_jsx(_components.code,{children:`hello-1`}),` and `,_jsx(_components.code,{children:`hello-2`}),` after they’re attached.`]}),`\n`,_jsx(Heading,{level:`3`,id:`configuration`,children:`Configuration`}),`\n`,_jsxs(_components.p,{children:[`Developer IDs are generated and validated against a workspace-wide `,_jsx(_components.strong,{children:`Developer ID configuration`}),`, editable on the `,_jsx(_components.a,{href:`https://app.dittowords.com/developers/configure-dev-ids`,children:`Developers page`}),`:`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Allowed characters`}),`: the set of characters permitted in a developer ID.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Character replacement`}),`: an ordered list of substring replacements applied to the source text during generation.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Casing adjustment`}),`: optionally transform the case of the source text. Options: lowercase, UPPERCASE, camelCase, PascalCase.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Maximum length`}),`: the maximum number of characters. Deduplication may append a short suffix `,_jsx(_components.em,{children:`after`}),` truncation, so generated IDs can exceed the limit by a few characters. If your system has a hard limit, set the maximum three or more characters below it. Manual edits over the limit are rejected.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`A scan proposes this configuration for you from the keys it finds in your code (see `,_jsx(_components.a,{href:`#what-a-scan-produces`,children:`What a scan produces`}),`). More detail: `,_jsx(_components.a,{href:`https://help.dittowords.com/en/articles/14529160-developer-id-configuration`,children:`Developer ID configuration`}),`.`]}),`\n`,_jsx(Heading,{level:`3`,id:`marking-text-as-integrated`,children:`Marking text as integrated`}),`\n`,_jsxs(_components.p,{children:[`Text items and library components can be marked `,_jsx(_components.strong,{children:`integrated`}),` to signal that their developer ID is in use in code: changing the ID would be a breaking change, and changing the text changes production copy.`]}),`\n`,_jsxs(_components.p,{children:[`The flag is a filter on every API endpoint that fetches text, on the CLI config (`,_jsx(_components.code,{children:`integrated: true`}),`), and on every webhook payload (`,_jsx(_components.code,{children:`integrated: boolean`}),`), so you can limit what reaches your codebase to text that is known to be in use.`]}),`\n`,_jsx(OptimizedImage,{src:`https://mintcdn.com/ditto/dDYr8fLNLi9vWSv6/images/dev-id-integrated-toggle.png?fit=max\u0026auto=format\u0026n=dDYr8fLNLi9vWSv6\u0026q=85\u0026s=6611b2ec1d9ec9dbb293b524c4fc8ac3`,alt:`The Integrated toggle in a text item's details panel`,style:{width:`100%`},\"data-og-width\":`383`,width:`383`,\"data-og-height\":`146`,height:`146`,\"data-path\":`images/dev-id-integrated-toggle.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/ditto/dDYr8fLNLi9vWSv6/images/dev-id-integrated-toggle.png?w=280\u0026fit=max\u0026auto=format\u0026n=dDYr8fLNLi9vWSv6\u0026q=85\u0026s=0574ac88379753d605cab9f737b8ed4d 280w, https://mintcdn.com/ditto/dDYr8fLNLi9vWSv6/images/dev-id-integrated-toggle.png?w=560\u0026fit=max\u0026auto=format\u0026n=dDYr8fLNLi9vWSv6\u0026q=85\u0026s=2876009eb4fdaf05c62233b9ad354929 560w, https://mintcdn.com/ditto/dDYr8fLNLi9vWSv6/images/dev-id-integrated-toggle.png?w=840\u0026fit=max\u0026auto=format\u0026n=dDYr8fLNLi9vWSv6\u0026q=85\u0026s=1fa5247b0e1d12346d7a2a6da3914d6a 840w, https://mintcdn.com/ditto/dDYr8fLNLi9vWSv6/images/dev-id-integrated-toggle.png?w=1100\u0026fit=max\u0026auto=format\u0026n=dDYr8fLNLi9vWSv6\u0026q=85\u0026s=65ae206e10973e24e0719785aad6ccd7 1100w, https://mintcdn.com/ditto/dDYr8fLNLi9vWSv6/images/dev-id-integrated-toggle.png?w=1650\u0026fit=max\u0026auto=format\u0026n=dDYr8fLNLi9vWSv6\u0026q=85\u0026s=1ecc428476fd67c5cfa77d5cdd433c6d 1650w, https://mintcdn.com/ditto/dDYr8fLNLi9vWSv6/images/dev-id-integrated-toggle.png?w=2500\u0026fit=max\u0026auto=format\u0026n=dDYr8fLNLi9vWSv6\u0026q=85\u0026s=9b891325f92ee14913318335be791c2b 2500w`}),`\n`,_jsx(Heading,{level:`3`,id:`where-to-find-and-edit-developer-ids`,children:`Where to find and edit developer IDs`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Projects`}),`: open the project’s main menu (top right of the project in the web app) and choose `,_jsx(_components.strong,{children:`Development integration`}),`.`]}),`\n`,_jsx(OptimizedImage,{src:`https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-menu.png?fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=eecd0d72bb4cc22d6d9d8923d0ec8f49`,alt:`Project menu with the Development integration entry`,style:{width:`100%`},\"data-og-width\":`598`,width:`598`,\"data-og-height\":`626`,height:`626`,\"data-path\":`images/project-menu.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-menu.png?w=280\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=bd488ebfec76b92bc09b62c082a950a5 280w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-menu.png?w=560\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=9b22dd8846c0acf47e205664dc584d42 560w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-menu.png?w=840\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=0114b85bc20f5f35a18006f1de29398d 840w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-menu.png?w=1100\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=d820b52f4bd6ed391c287ab35ca7273b 1100w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-menu.png?w=1650\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=0f865f5d42c3e45e4d69525fffd9c6f2 1650w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-menu.png?w=2500\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=c8146e6e2caf0ff2e3b00556080ef54c 2500w`}),`\n`,_jsx(OptimizedImage,{src:`https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-dev-integration-modal.png?fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=6a5bf52d92416a6bdf926de0f3ef041a`,alt:`Project developer integration modal showing the project developer ID`,style:{width:`100%`},\"data-og-width\":`848`,width:`848`,\"data-og-height\":`446`,height:`446`,\"data-path\":`images/project-dev-integration-modal.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-dev-integration-modal.png?w=280\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=76b4afe48f81337101e6835c72a329be 280w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-dev-integration-modal.png?w=560\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=5e239e0a28d2e82016e4d61ebfb3a1a7 560w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-dev-integration-modal.png?w=840\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=cffe9681bbb59b1d09eb87a47fa48e9e 840w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-dev-integration-modal.png?w=1100\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=afcced398c7f8b45dea4be4885d7780e 1100w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-dev-integration-modal.png?w=1650\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=02dcefc671c1a491f8b86aec84ed4897 1650w, https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/project-dev-integration-modal.png?w=2500\u0026fit=max\u0026auto=format\u0026n=pXBmKmaeW4wg1sef\u0026q=85\u0026s=c859dd749c379c0630243dd287d521fd 2500w`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Text items and library components`}),`: select the item and edit the `,_jsx(_components.strong,{children:`Developer ID`}),` field in the details panel.`]}),`\n`,_jsx(OptimizedImage,{src:`https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-edit-text-item.png?fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=b26b634a5d45f251f3eefba100a66100`,alt:`Editing a text item's developer ID`,\"data-og-width\":`1918`,width:`1918`,\"data-og-height\":`1274`,height:`1274`,\"data-path\":`images/dev-id-edit-text-item.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-edit-text-item.png?w=280\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=cd0929d5a23703451f6379c84c95e8a2 280w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-edit-text-item.png?w=560\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=16bd107299ab4b71d9c15e069f89543e 560w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-edit-text-item.png?w=840\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=8b00b1dc57096a03b0ecc0f48e252d69 840w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-edit-text-item.png?w=1100\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=9780a706eaafc1757bca96d0f3484524 1100w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-edit-text-item.png?w=1650\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=be7c36c9dd44731275013de26c4780e9 1650w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-edit-text-item.png?w=2500\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=a0399dd903db0630ef5d0421923f154c 2500w`}),`\n`,_jsxs(_components.p,{children:[`Select several items to open the `,_jsx(_components.strong,{children:`Bulk editor`}),`, which edits many IDs at once and supports operations such as adding a prefix. Details: `,_jsx(_components.a,{href:`https://help.dittowords.com/en/articles/14792979-developer-id-bulk-editor`,children:`Developer ID bulk editor`}),`.`]}),`\n`,_jsx(OptimizedImage,{src:`https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-bulk-editor.png?fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=c4cd2342a4d353da5e2cdfc4a58cb61e`,alt:`The developer ID bulk editor`,\"data-og-width\":`929`,width:`929`,\"data-og-height\":`634`,height:`634`,\"data-path\":`images/dev-id-bulk-editor.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-bulk-editor.png?w=280\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=9a14e8ed6c50f14d9010dbd2a9b551ba 280w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-bulk-editor.png?w=560\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=2eed99b7505be6fa0aff3cfb1ac17cc8 560w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-bulk-editor.png?w=840\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=169dc33378768d35a462fae04f406b77 840w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-bulk-editor.png?w=1100\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=c6ad4870a530a028f3b3487df2580e94 1100w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-bulk-editor.png?w=1650\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=53509cc4e37f39a695c69d18196d1ccd 1650w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-bulk-editor.png?w=2500\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=7d4a9846d0cfd5e4ee3968dcd4f79790 2500w`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Library folders`}),`: open the folder’s parent, choose `,_jsx(_components.strong,{children:`Folder properties…`}),` from the folder’s menu, and edit the `,_jsx(_components.strong,{children:`Developer ID`}),` in the details panel.`]}),`\n`,_jsx(OptimizedImage,{src:`https://mintcdn.com/ditto/pXBmKmaeW4wg1sef/images/library-folder-dev-ids.gif?s=97a5dbf0aeea0eaf02dfd4af980d26ed`,alt:`Editing a library folder's developer ID`,style:{width:`100%`},\"data-og-width\":`1328`,width:`1328`,\"data-og-height\":`774`,height:`774`,\"data-path\":`images/library-folder-dev-ids.gif`,\"data-optimize\":`true`,\"data-opv\":`3`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Variants`}),`: open `,_jsx(_components.a,{href:`https://app.dittowords.com/variants`,children:`Variants`}),`, select a variant, and edit the `,_jsx(_components.strong,{children:`Developer ID`}),` in the details panel.`]}),`\n`,_jsx(OptimizedImage,{src:`https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-variant.png?fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=008ec03e66c2413ba96439c4b0bc0726`,alt:`Editing a variant's developer ID`,\"data-og-width\":`2516`,width:`2516`,\"data-og-height\":`1042`,height:`1042`,\"data-path\":`images/dev-id-variant.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-variant.png?w=280\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=8dbffd24d9b9e30a497dd14d3f4d0cea 280w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-variant.png?w=560\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=8227663502da9f375f21fd0563b686d9 560w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-variant.png?w=840\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=d4b5405a3eabb50b82b937d2e0569029 840w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-variant.png?w=1100\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=ca4b2f7c98aad6d0927dffab064ba9a8 1100w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-variant.png?w=1650\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=b5bb0dbecb650ad49bc8ea2da6be9335 1650w, https://mintcdn.com/ditto/7aMEipg7bpvcHX8V/images/dev-id-variant.png?w=2500\u0026fit=max\u0026auto=format\u0026n=7aMEipg7bpvcHX8V\u0026q=85\u0026s=a55315422758bd590a9cde8e6a378748 2500w`}),`\n`,_jsx(Heading,{level:`2`,id:`style-guides-and-rules`,children:`Style guides and rules`}),`\n`,_jsxs(_components.p,{children:[`A `,_jsx(_components.strong,{children:`style guide`}),` is a named ruleset for how your product’s text reads. A workspace can have several. Each has a description and an `,_jsx(_components.strong,{children:`enabled by default`}),` flag; enabled-by-default guides apply to every project unless a project turns them off, and to the component library, which has no per-guide setting of its own.`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`A style guide is divided into `,_jsx(_components.strong,{children:`sections`}),`, each of kind `,_jsx(_components.code,{children:`rules`}),` or `,_jsx(_components.code,{children:`wordlist`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`A `,_jsx(_components.strong,{children:`rule`}),` (in a `,_jsx(_components.code,{children:`rules`}),` section) has a `,_jsx(_components.code,{children:`name`}),`, a `,_jsx(_components.code,{children:`description`}),` of what to do, one or more `,_jsx(_components.code,{children:`examples`}),` as from → to pairs, optional `,_jsx(_components.code,{children:`tags`}),` that limit it to text carrying those tags, and an `,_jsx(_components.code,{children:`enabled`}),` flag.`]}),`\n`,_jsxs(_components.li,{children:[`A `,_jsx(_components.strong,{children:`word list entry`}),` (in a `,_jsx(_components.code,{children:`wordlist`}),` section) has a preferred `,_jsx(_components.code,{children:`term`}),`, `,_jsx(_components.code,{children:`disallowed`}),` alternatives, and a `,_jsx(_components.code,{children:`description`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`A style guide can be `,_jsx(_components.strong,{children:`scoped to a variant with a locale code`}),`, which is how locale-specific rules (formality, terminology) are attached to a language.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Agents fetch rules with `,_jsx(_components.code,{children:`get_styleguide_rules`}),` and check text with `,_jsx(_components.code,{children:`suggest_edit`}),`; the `,_jsx(_components.a,{href:`/additional-tools/github-pr-review-bot`,children:`PR review bot`}),` enforces them on pull requests; the Ditto app applies them through Magic Edit and Magic Translate. After a scan, Ditto drafts a starter style guide from your strings; see `,_jsx(_components.a,{href:`/get-started/scan-your-repo#get-a-first-style-guide`,children:`Get a first style guide from your scan`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`variants-and-locales`,children:`Variants and locales`}),`\n`,_jsxs(_components.p,{children:[`A `,_jsx(_components.strong,{children:`variant`}),` is a named alternative for a text item’s text. Every text item and library component has base text and any number of variant texts. Variants are workspace-level objects with a name, description, developer ID, and optional `,_jsx(_components.strong,{children:`locale code`}),`.`]}),`\n`,_jsxs(_components.p,{children:[`A `,_jsx(_components.strong,{children:`locale`}),` is simply a variant with a locale code (`,_jsx(_components.code,{children:`de-DE`}),`, `,_jsx(_components.code,{children:`fr`}),`, `,_jsx(_components.code,{children:`es-419`}),`). Ditto’s localization features key on it: Magic Translate uses it as the target language, locale-scoped style guides attach to it, and the CLI’s `,_jsx(_components.code,{children:`iosLocales`}),` and `,_jsx(_components.code,{children:`androidLocales`}),` map it to platform locale directories. At most one variant per workspace holds a given locale code.`]}),`\n`,_jsx(_components.p,{children:`Variants without a locale code cover other uses, such as tone or A/B copy.`}),`\n`,_jsxs(_components.p,{children:[`The `,_jsx(_components.strong,{children:`base locale`}),` is the language of your base text, set in a project’s or the library’s `,_jsx(_components.strong,{children:`Variant \u0026 locale settings`}),`. Set it when your source copy is not English.`]}),`\n`,_jsxs(_components.p,{children:[`How to add locales, get translations, and pull localized files: `,_jsx(_components.a,{href:`/guides/set-up-localization`,children:`Set up localization`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`variables`,children:`Variables`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Variables`}),` are reusable placeholders for values your app fills in at runtime. Write one into text as `,_jsx(_components.code,{children:`{{variable_name}}`}),`. Each variable has a type: `,_jsx(_components.code,{children:`string`}),` or `,_jsx(_components.code,{children:`number`}),` (with an example and optional fallback), `,_jsx(_components.code,{children:`hyperlink`}),` (link text and URL), `,_jsx(_components.code,{children:`list`}),` (a set of possible values), or `,_jsx(_components.code,{children:`map`}),` (key-value pairs).`]}),`\n`,_jsx(_components.p,{children:`A variable’s name is its developer ID: letters, numbers, and underscores, unique in the workspace.`}),`\n`,_jsxs(_components.p,{children:[`The API, CLI, and manual exports render variables in each string format’s native placeholder syntax (for example `,_jsx(_components.code,{children:`{username}`}),` in ICU JSON, `,_jsx(_components.code,{children:`%1$@`}),` in iOS `,_jsx(_components.code,{children:`.strings`}),`, `,_jsx(_components.code,{children:`\u003cxliff:g>`}),` in Android XML). A scan detects the variables in your strings and creates them. More detail: `,_jsx(_components.a,{href:`https://help.dittowords.com/en/articles/11657688-variables`,children:`Variables`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`plurals`,children:`Plurals`}),`\n`,_jsxs(_components.p,{children:[`A text item can define `,_jsx(_components.strong,{children:`plural forms`}),` (`,_jsx(_components.code,{children:`zero`}),`, `,_jsx(_components.code,{children:`one`}),`, `,_jsx(_components.code,{children:`two`}),`, `,_jsx(_components.code,{children:`few`}),`, `,_jsx(_components.code,{children:`many`}),`, `,_jsx(_components.code,{children:`other`}),`) so your app renders different copy for different counts: “No results”, “1 result”, “10 results”.`]}),`\n`,_jsxs(_components.p,{children:[`The API returns the base form (`,_jsx(_components.code,{children:`pluralForm: null`}),`) and each defined plural form as separate objects. Following i18next conventions, each plural form’s developer ID is the text item’s developer ID with a suffix, for example `,_jsx(_components.code,{children:`results_one`}),`. In your code you reference the main developer ID and pass a `,_jsx(_components.code,{children:`count`}),`; the i18n library picks the form.`]}),`\n`,_jsxs(_components.p,{children:[`Plurals can contain variables and can be translated per variant. Each string format renders plurals natively (`,_jsx(_components.code,{children:`.stringsdict`}),` on iOS, `,_jsx(_components.code,{children:`\u003cplurals>`}),` on Android, `,_jsx(_components.code,{children:`{count, plural, …}`}),` in ICU). More detail: `,_jsx(_components.a,{href:`https://help.dittowords.com/en/articles/13460485-plurals`,children:`Plurals`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`tags-and-statuses`,children:`Tags and statuses`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Tags`}),` are labels on text items and library components. Style guide rules can be limited to tagged text, and the API, CLI, and `,_jsx(_components.code,{children:`search_ditto_text`}),` filter by tag. Only tags that already exist in the workspace can be used on a rule; `,_jsx(_components.code,{children:`get_workspace_tags`}),` lists them. A scan proposes a tag vocabulary for your strings.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Statuses`}),` track text through your workflow, from first draft to ready to ship. The default set is `,_jsx(_components.code,{children:`NONE`}),`, `,_jsx(_components.code,{children:`WIP`}),`, `,_jsx(_components.code,{children:`REVIEW`}),`, `,_jsx(_components.code,{children:`FINAL`}),`, but a workspace can rename or replace it, so call `,_jsx(_components.code,{children:`list_statuses`}),` (MCP) or `,_jsx(_components.code,{children:`GET /v2/statuses`}),` before setting or filtering by status. Filtering `,_jsx(_components.code,{children:`pull`}),` to `,_jsx(_components.code,{children:`FINAL`}),` keeps unreviewed text out of production.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`what-a-scan-produces`,children:`What a scan produces`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`npx @dittowords/cli scan \u003cpath>`}),` extracts `,_jsx(_components.strong,{children:`candidate strings`}),` from your code and uploads them for review. A candidate is one string with where it was found, how it was found (markup text, an attribute value, a value in a localization resource file, or another string position), its translation key and locale if it came from a localization file, and a few surrounding lines of source.`]}),`\n`,_jsx(_components.p,{children:`Ditto then:`}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Classifies`}),` each candidate as user-facing or not.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Proposes a content system`}),` for the user-facing strings: a tag vocabulary, a naming scheme for components, a folder structure, the variants and locales found in your translation files, and a developer ID configuration derived from your existing keys. You review and adjust this before anything is created.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Creates`}),` the library components (in folders, with tags), the variables they use, and one variant per locale.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Drafts a style guide`}),` from a sample of the strings, for you to review and enable.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Details and commands: `,_jsx(_components.a,{href:`/get-started/scan-your-repo`,children:`Scan your repo`}),`.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Key concepts","description":"Definitions for Ditto's data model: projects, text items, library components, component versioning, developer IDs, style guides, variants, variables, plurals, and scans.","keywords":["text items","library components","component versioning","developer IDs","variants","variables","plurals","style guides","blocks"],"href":"/concepts"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Projects, text items, and the component library","slug":"projects-text-items-and-the-component-library","depth":2,"children":[]},{"title":"Component versioning","slug":"component-versioning","depth":2,"children":[]},{"title":"Developer IDs","slug":"developer-ids","depth":2,"children":[{"title":"Uniqueness","slug":"uniqueness","depth":3,"children":[]},{"title":"Configuration","slug":"configuration","depth":3,"children":[]},{"title":"Marking text as integrated","slug":"marking-text-as-integrated","depth":3,"children":[]},{"title":"Where to find and edit developer IDs","slug":"where-to-find-and-edit-developer-ids","depth":3,"children":[]}]},{"title":"Style guides and rules","slug":"style-guides-and-rules","depth":2,"children":[]},{"title":"Variants and locales","slug":"variants-and-locales","depth":2,"children":[]},{"title":"Variables","slug":"variables","depth":2,"children":[]},{"title":"Plurals","slug":"plurals","depth":2,"children":[]},{"title":"Tags and statuses","slug":"tags-and-statuses","depth":2,"children":[]},{"title":"What a scan produces","slug":"what-a-scan-produces","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"ditto","siteName":"Ditto","title":"Key concepts - Ditto","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-11-02T20:06:48.360Z","originalFileLocation":"concepts.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Definitions for Ditto\u0026#x27;s data model: projects, text items, library components, component versioning, developer IDs, style guides, variants, variables, plurals, and scans.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Ditto\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Key concepts - Ditto\">\u003cmeta data-mint-head property=\"og:description\" content=\"Definitions for Ditto\u0026#x27;s data model: projects, text items, library components, component versioning, developer IDs, style guides, variants, variables, plurals, and scans.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Ditto\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://developer.dittowords.com/concepts\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://ditto.mintlify.site/_mintlify/api/og/v2?division=Concepts\u0026amp;title=Key+concepts\u0026amp;description=Definitions+for+Ditto%27s+data+model%3A+projects%2C+text+items%2C+library+components%2C+component+versioning%2C+developer+IDs%2C+style+guides%2C+variants%2C+variables%2C+plurals%2C+a\u0026amp;theme=cafcf8215feabbb48baf53ae\u0026amp;signature=632ff55af4e65f7bcb27f8df895822eaa3d239c030de2f3a3041c4ead4a14b36\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Key concepts - Ditto\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Definitions for Ditto\u0026#x27;s data model: projects, text items, library components, component versioning, developer IDs, style guides, variants, variables, plurals, and scans.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://ditto.mintlify.site/_mintlify/api/og/v2?division=Concepts\u0026amp;title=Key+concepts\u0026amp;description=Definitions+for+Ditto%27s+data+model%3A+projects%2C+text+items%2C+library+components%2C+component+versioning%2C+developer+IDs%2C+style+guides%2C+variants%2C+variables%2C+plurals%2C+a\u0026amp;theme=cafcf8215feabbb48baf53ae\u0026amp;signature=632ff55af4e65f7bcb27f8df895822eaa3d239c030de2f3a3041c4ead4a14b36\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/ditto/sayH8aGeMpv32blB/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Ditto\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#E2B103\">\u003clink data-mint-head rel=\"canonical\" href=\"https://developer.dittowords.com/concepts\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/concepts.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://www.dittowords.com/#organization\",\"name\":\"Ditto\",\"url\":\"https://www.dittowords.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://developer.dittowords.com/logo/ditto_logo.svg\"},\"sameAs\":[\"https://twitter.com/dittowords\",\"https://github.com/dittowords\",\"https://www.linkedin.com/company/ditto-app\"]},{\"@type\":\"WebSite\",\"@id\":\"https://developer.dittowords.com#website\",\"name\":\"Ditto\",\"url\":\"https://developer.dittowords.com\",\"publisher\":{\"@id\":\"https://www.dittowords.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://developer.dittowords.com/concepts#webpage\",\"url\":\"https://developer.dittowords.com/concepts\",\"name\":\"Key concepts\",\"description\":\"Definitions for Ditto's data model: projects, text items, library components, component versioning, developer IDs, style guides, variants, variables, plurals, and scans.\",\"dateModified\":\"2026-11-02T20:06:48.360Z\",\"isPartOf\":{\"@id\":\"https://developer.dittowords.com#website\"},\"breadcrumb\":{\"@id\":\"https://developer.dittowords.com/concepts#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://developer.dittowords.com/concepts#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Ditto\",\"item\":\"https://developer.dittowords.com\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Key concepts\",\"item\":\"https://developer.dittowords.com/concepts\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://developer.dittowords.com/concepts#article\",\"headline\":\"Key concepts\",\"name\":\"Key concepts\",\"description\":\"Definitions for Ditto's data model: projects, text items, library components, component versioning, developer IDs, style guides, variants, variables, plurals, and scans.\",\"url\":\"https://developer.dittowords.com/concepts\",\"mainEntityOfPage\":{\"@id\":\"https://developer.dittowords.com/concepts#webpage\"},\"dateModified\":\"2026-11-02T20:06:48.360Z\",\"keywords\":\"text items, library components, component versioning, developer IDs, variants, variables, plurals, style guides, blocks\",\"publisher\":{\"@id\":\"https://www.dittowords.com/#organization\"},\"isPartOf\":{\"@id\":\"https://developer.dittowords.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Ditto\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Ditto","nav":{"global":{"anchors":[{"anchor":"Help Guides","icon":"book-open-cover","href":"https://help.dittowords.com/en/"},{"anchor":"What's New","icon":"newspaper","href":"https://www.dittowords.com/whats-new"},{"anchor":"Figma Plugin","icon":"figma","href":"https://www.figma.com/community/plugin/798826066406007173/ditto-manage-copy-from-design-to-production"}]},"tabs":[{"tab":"Documentation","groups":[{"group":"Start here","pages":[{"title":"Ditto developer documentation","sidebarTitle":"Introduction","href":"/introduction"},{"title":"Scan your repo","href":"/get-started/scan-your-repo"},{"title":"Connect Figma files","href":"/guides/connect-figma-files"},{"title":"Set up localization","description":"Manage and automate localization in Ditto: add locales as variants, translate with AI, preview in design, and bring localized string files to code.","href":"/guides/set-up-localization"}]},{"group":"Concepts","pages":[{"title":"Key concepts","href":"/concepts"}]},{"group":"CLI Reference","pages":[{"title":"Install and log in to the Ditto CLI","sidebarTitle":"Install and log in","description":"Install the Ditto CLI and log in with your Ditto account so you can scan a codebase and pull string files.","href":"/cli-reference/authentication"},{"title":"Ditto CLI commands","sidebarTitle":"Commands","href":"/cli-reference/commands"},{"title":"Ditto CLI configuration","sidebarTitle":"Configuration","href":"/cli-reference/configuration"},{"title":"Ditto CLI output files","sidebarTitle":"Files","href":"/cli-reference/files"}]},{"group":"API Reference","pages":[{"title":"Ditto API authentication","sidebarTitle":"Authentication","href":"/api-reference/authentication"},{"group":"Endpoints","pages":[{"group":"Activity","pages":[{"openapi":"openapi.json get /activity","title":"Fetch activity","href":"/api-reference/activity/get-activity"}]},{"group":"Bases","tag":"BETA","pages":[{"openapi":"openapi.json get /bases","tag":"BETA","title":"Fetch bases","href":"/api-reference/bases/get-bases"},{"openapi":"openapi.json get /bases/{baseId}","tag":"BETA","title":"Fetch a base","href":"/api-reference/bases/get-base"},{"openapi":"openapi.json post /bases","tag":"BETA","title":"Create bases","href":"/api-reference/bases/create-bases"},{"openapi":"openapi.json patch /bases/{baseId}","tag":"BETA","title":"Update a base","href":"/api-reference/bases/patch-bases"}]},{"group":"Components","pages":[{"openapi":"openapi.json get /components","title":"Fetch components","href":"/api-reference/components/get-components"},{"openapi":"openapi.json get /components/export","title":"Export components","href":"/api-reference/components/export-components"},{"openapi":"openapi.json post /components","title":"Create components","href":"/api-reference/components/create-components"},{"openapi":"openapi.json patch /components","title":"Update components","href":"/api-reference/components/patch-components"},{"openapi":"openapi.json delete /components","title":"Delete components","href":"/api-reference/components/delete-components"},{"group":"Text Item Linking","pages":[{"openapi":"openapi.json post /components/publish","title":"Publish a text item to the library","href":"/api-reference/components/publish-component"},{"openapi":"openapi.json patch /components/link","title":"Link text items to a component","href":"/api-reference/components/link-components"},{"openapi":"openapi.json patch /components/unlink","title":"Unlink text items from a component","href":"/api-reference/components/unlink-components"}]},{"group":"Versioning","pages":[{"openapi":"openapi.json get /components/staged","title":"Fetch unpublished component changes","href":"/api-reference/components/get-unpublished-component-changes"},{"openapi":"openapi.json patch /components/staged","title":"Save unpublished component changes","href":"/api-reference/components/save-unpublished-component-changes"},{"openapi":"openapi.json delete /components/staged","title":"Discard unpublished component changes","href":"/api-reference/components/discard-unpublished-component-changes"},{"openapi":"openapi.json post /components/staged/publish","title":"Publish component changes","href":"/api-reference/components/publish-component-changes"},{"openapi":"openapi.json patch /components/upgrade","title":"Upgrade component instances","href":"/api-reference/components/upgrade-component-instances"}]}]},{"group":"Component Folders","pages":[{"openapi":"openapi.json get /componentFolders","title":"Fetch component folders","href":"/api-reference/component-folders/get-component-folders"},{"openapi":"openapi.json post /componentFolders","title":"Create component folders","href":"/api-reference/component-folders/create-component-folders"},{"openapi":"openapi.json patch /componentFolders","title":"Update component folders","href":"/api-reference/component-folders/patch-component-folders"},{"openapi":"openapi.json delete /componentFolders","title":"Delete component folders","href":"/api-reference/component-folders/delete-component-folders"}]},{"group":"Projects","pages":[{"openapi":"openapi.json get /projects","title":"Fetch projects","href":"/api-reference/projects/get-projects"},{"openapi":"openapi.json post /projects","title":"Create a project","href":"/api-reference/projects/create-projects"},{"openapi":"openapi.json patch /projects/{projectId}","title":"Update a project","href":"/api-reference/projects/patch-projects"},{"openapi":"openapi.json post /projects/{projectId}/publish","tag":"BETA","title":"Publish to base","href":"/api-reference/projects/publish-project"},{"openapi":"openapi.json post /projects/{projectId}/updateFromBase","tag":"BETA","sidebarTitle":"Update from base","title":"Update text items from base","href":"/api-reference/projects/update-text-items-from-base"}]},{"group":"Project Blocks","pages":[{"openapi":"openapi.json get /projectBlocks","title":"Fetch project blocks","href":"/api-reference/project-blocks/get-project-blocks"},{"openapi":"openapi.json post /projectBlocks","title":"Create project blocks","href":"/api-reference/project-blocks/create-project-blocks"},{"openapi":"openapi.json patch /projectBlocks","title":"Update project blocks","href":"/api-reference/project-blocks/patch-project-blocks"},{"openapi":"openapi.json delete /projectBlocks","title":"Delete project blocks","href":"/api-reference/project-blocks/delete-project-blocks"}]},{"group":"Statuses","pages":[{"openapi":"openapi.json get /statuses","title":"Fetch statuses","href":"/api-reference/statuses/get-statuses"}]},{"group":"Style guides","pages":[{"title":"Get style guides","openapi":"openapi.json get /styleguides","href":"/api-reference/styleguides/get-styleguides"},{"openapi":"openapi.json post /styleguides","title":"Create a style guide","href":"/api-reference/styleguides/create-styleguides"},{"openapi":"openapi.json patch /styleguides","title":"Update a style guide","href":"/api-reference/styleguides/patch-styleguides"},{"openapi":"openapi.json post /styleguides/rules","title":"Create style guide rules","href":"/api-reference/styleguides/create-styleguide-rules"},{"openapi":"openapi.json patch /styleguides/rules","title":"Update style guide rules","href":"/api-reference/styleguides/patch-styleguide-rules"},{"openapi":"openapi.json delete /styleguides/rules","title":"Delete style guide rules","href":"/api-reference/styleguides/delete-styleguide-rules"}]},{"group":"Text Items","pages":[{"openapi":"openapi.json get /textItems","title":"Fetch text items","href":"/api-reference/text-items/get-text-items"},{"openapi":"openapi.json get /textItems/export","title":"Export text items","href":"/api-reference/text-items/export-text-items"},{"openapi":"openapi.json post /textItems","title":"Create text items","href":"/api-reference/text-items/create-text-items"},{"openapi":"openapi.json post /textItems/fromBase","tag":"BETA","title":"Create text items from a base","href":"/api-reference/text-items/create-text-items-from-base"},{"openapi":"openapi.json patch /textItems","title":"Update text items","href":"/api-reference/text-items/patch-text-items"},{"openapi":"openapi.json put /textItems","title":"Update text items","href":"/api-reference/text-items/update-text-items"},{"openapi":"openapi.json delete /textItems","title":"Delete text items","href":"/api-reference/text-items/delete-text-items"},{"openapi":"openapi.json post /textItems/linkFigmaNodes","title":"Link Figma nodes to text items","href":"/api-reference/text-items/link-figma-nodes"}]},{"group":"Variables","pages":[{"openapi":"openapi.json get /variables","title":"Fetch variables","href":"/api-reference/variables/get-variables"},{"openapi":"openapi.json post /variables","title":"Create variables","href":"/api-reference/variables/create-variables"},{"openapi":"openapi.json patch /variables","title":"Update variables","href":"/api-reference/variables/patch-variables"},{"openapi":"openapi.json delete /variables","title":"Delete variables","href":"/api-reference/variables/delete-variables"}]},{"group":"Variants","pages":[{"openapi":"openapi.json get /variants","title":"Fetch variants","href":"/api-reference/variants/get-variants"},{"openapi":"openapi.json post /variants","title":"Create variants","href":"/api-reference/variants/create-variants"},{"openapi":"openapi.json patch /variants","title":"Update variants","href":"/api-reference/variants/patch-variants"},{"openapi":"openapi.json delete /variants","title":"Delete variants","href":"/api-reference/variants/delete-variants"}]}]}]},{"group":"String Formats","pages":[{"title":"JSON","icon":"js","href":"/string-formats/json"},{"title":"iOS string formats","sidebarTitle":"iOS Strings","icon":"apple","href":"/string-formats/ios"},{"title":"Android XML string format","sidebarTitle":"Android XML","icon":"android","href":"/string-formats/android"}]},{"group":"MCP Reference","pages":[{"title":"Ditto MCP server overview","sidebarTitle":"Overview","href":"/mcp-reference/overview"},{"title":"Connect the Ditto MCP","sidebarTitle":"Setup","href":"/guides/connect-the-mcp"},{"title":"Ditto MCP tools","sidebarTitle":"Tools","href":"/mcp-reference/tools"},{"title":"Agent instructions","href":"/guides/agent-instructions"}]},{"group":"Ditto Specs CLI","pages":[{"title":"Ditto Specs overview","sidebarTitle":"Overview","href":"/ditto-specs-cli-reference/overview"},{"title":"Ditto Specs CLI setup","sidebarTitle":"Setup","href":"/ditto-specs-cli-reference/setup"},{"title":"Ditto Specs CLI commands","sidebarTitle":"Commands","href":"/ditto-specs-cli-reference/commands"},{"title":"Ditto Spec Files","sidebarTitle":"Spec files","href":"/ditto-specs-cli-reference/spec-files"},{"title":"Ditto Specs agent skills","sidebarTitle":"Agent skills","href":"/ditto-specs-cli-reference/agent-skills"}]},{"group":"Additional Tools","pages":[{"title":"GitHub PR Review Bot","href":"/additional-tools/github-pr-review-bot"},{"title":"Webhooks","href":"/additional-tools/webhooks"},{"title":"GitHub Action","href":"/additional-tools/gh-action"}]},{"group":"Legacy","pages":[{"group":"API Reference","pages":[{"title":"Legacy API authentication","sidebarTitle":"Authentication","href":"/legacy/api-reference-legacy/authentication"},{"group":"Endpoints","pages":[{"group":"Components","pages":[{"openapi":"openapi-legacy.json get /components","title":"Fetch component text","href":"/legacy/api-reference-legacy/components/fetch-component-text"},{"openapi":"openapi-legacy.json post /components/file","title":"Import components via a file","href":"/legacy/api-reference-legacy/components/import-components-via-a-file"},{"openapi":"openapi-legacy.json put /components","title":"Update component text variant","href":"/legacy/api-reference-legacy/components/update-component-text-variant"}]},{"group":"Component Folders","pages":[{"openapi":"openapi-legacy.json get /component-folders","title":"Fetch component folders","href":"/legacy/api-reference-legacy/components-folders/fetch-component-folders"},{"openapi":"openapi-legacy.json get /component-folders/{api_id}/components","title":"Fetch component text by component folder","href":"/legacy/api-reference-legacy/components-folders/fetch-component-text-by-component-folder"}]},{"group":"Projects","pages":[{"openapi":"openapi-legacy.json get /projects/{id}/components","title":"Fetch component text by project id","href":"/legacy/api-reference-legacy/projects/fetch-component-text-by-project-id"},{"openapi":"openapi-legacy.json get /projects","title":"Fetch projects","href":"/legacy/api-reference-legacy/projects/fetch-projects"},{"openapi":"openapi-legacy.json get /projects/{id}","title":"Fetch text by project id","href":"/legacy/api-reference-legacy/projects/fetch-text-by-project-id"}]},{"group":"Project Folders","pages":[{"openapi":"openapi-legacy.json get /project-folders","title":"Fetch project folders","href":"/legacy/api-reference-legacy/project-folders/fetch-project-folders"},{"openapi":"openapi-legacy.json get /project-folders/{folder_id}/projects","title":"Fetch projects by project folder id","href":"/legacy/api-reference-legacy/project-folders/fetch-projects-by-project-folder-id"}]},{"group":"Variables","pages":[{"openapi":"openapi-legacy.json get /variables","title":"Fetch variables","href":"/legacy/api-reference-legacy/variables/fetch-variables"}]},{"group":"Variants","pages":[{"openapi":"openapi-legacy.json get /variants","title":"Fetch variants","href":"/legacy/api-reference-legacy/variants/fetch-variants"}]}]}]},{"group":"CLI Reference","pages":[{"title":"Legacy CLI installation","sidebarTitle":"Installation","href":"/legacy/cli-reference-legacy/authentication"},{"title":"Legacy CLI configuration","sidebarTitle":"Configuration","href":"/legacy/cli-reference-legacy/configuration"},{"title":"Legacy CLI output files","sidebarTitle":"Files","href":"/legacy/cli-reference-legacy/files"}]},{"title":"Legacy tools","href":"/legacy/legacy-tools"},{"title":"Example apps","href":"/example-apps"}]}]},{"tab":"Feedback \u0026 Support","pages":[{"title":"Feedback \u0026 Support","href":"/feedback-support/feedback-support"}]}]},"basePath":"","title":"Key concepts - Ditto","buildId":"dpl_FWZhTb2vab28gFGFH4ojka6qEznQ","deploymentId":"dpl_FWZhTb2vab28gFGFH4ojka6qEznQ","dynamics":["a74be98ba7"],"chunks":["zoom-image-lightbox-CE4cYsJE.js","lightbox-handle-r17bVUz3.js"]}