Improve apps (#19256)

- simplify the base application template
- remove --exhaustive option and replace by a --example option like in
next.js https://nextjs.org/docs/app/api-reference/cli
- Fix some bugs and logs
- add a post-card app in twenty-apps/examples/
This commit is contained in:
martmull
2026-04-03 14:44:03 +02:00
committed by GitHub
parent 2ff2c39cf4
commit 119014f86d
178 changed files with 12334 additions and 2508 deletions
@@ -81,7 +81,7 @@ import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
export default defineApplication({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d',
displayName: 'My Twenty App',
description: 'My first Twenty app',
icon: 'IconWorld',
@@ -717,6 +717,13 @@ Key points:
Front components are React components that render directly inside Twenty's UI. They run in an **isolated Web Worker** using Remote DOM — your code is sandboxed but renders natively in the page, not in an iframe.
#### Where front components can be used
Front components can render in two locations within Twenty:
- **Side panel** — Non-headless front components open in the right-hand side panel. This is the default behavior when a front component is triggered from the command menu.
- **Widgets (dashboards and record pages)** — Front components can be embedded as widgets inside page layouts. When configuring a dashboard or a record page layout, users can add a front component widget.
#### Basic example
The quickest way to see a front component in action is to register it as a **command**. Adding a `command` field with `isPinned: true` makes it appear as a quick-action button in the top-right corner of the page — no page layout needed:
@@ -774,9 +781,13 @@ Click it to render the component inline.
Beyond commands, you can embed a front component directly into a record page by adding it as a widget in a **page layout**. See the [definePageLayout](#definepagelayout) section for details.
#### Headless components (`isHeadless: true`)
#### Headless vs non-headless
Headless components render no visible UI but still run React logic. This is useful for **effect components** — components that perform side effects when mounted, such as syncing data, starting a timer, listening to events, or triggering a notification.
Front components come in two rendering modes controlled by the `isHeadless` option:
**Non-headless (default)** — The component renders a visible UI. When triggered from the command menu it opens in the side panel. This is the default behavior when `isHeadless` is `false` or omitted.
**Headless (`isHeadless: true`)** — The component mounts invisibly in the background. It does not open the side panel. Headless components are designed for actions that execute logic and then unmount themselves — for example, running an async task, navigating to a page, or showing a confirmation modal. They pair naturally with the SDK Command components described below.
```tsx src/front-components/sync-tracker.tsx
import { defineFrontComponent, useRecordId, enqueueSnackbar } from 'twenty-sdk';
@@ -803,6 +814,89 @@ export default defineFrontComponent({
Because the component returns `null`, Twenty skips rendering a container for it — no empty space appears in the layout. The component still has access to all hooks and the host communication API.
#### SDK Command components
The `twenty-sdk` package provides four Command helper components designed for headless front components. Each component executes an action on mount, handles errors by showing a snackbar notification, and automatically unmounts the front component when done.
Import them from `twenty-sdk/command`:
- **`Command`** — Runs an async callback via the `execute` prop.
- **`CommandLink`** — Navigates to an app path. Props: `to`, `params`, `queryParams`, `options`.
- **`CommandModal`** — Opens a confirmation modal. If the user confirms, executes the `execute` callback. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
- **`CommandOpenSidePanelPage`** — Opens a specific side panel page. Props: `page`, `pageTitle`, `pageIcon`.
Here is a full example of a headless front component using `Command` to run an action from the command menu:
```tsx src/front-components/run-action.tsx
import { defineFrontComponent } from 'twenty-sdk';
import { Command } from 'twenty-sdk/command';
import { CoreApiClient } from 'twenty-sdk/clients';
const RunAction = () => {
const execute = async () => {
const client = new CoreApiClient();
await client.mutation({
createTask: {
__args: { data: { title: 'Created by my app' } },
id: true,
},
});
};
return <Command execute={execute} />;
};
export default defineFrontComponent({
universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
name: 'run-action',
description: 'Creates a task from the command menu',
component: RunAction,
isHeadless: true,
command: {
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
icon: 'IconPlayerPlay',
},
});
```
And an example using `CommandModal` to ask for confirmation before executing:
```tsx src/front-components/delete-draft.tsx
import { defineFrontComponent } from 'twenty-sdk';
import { CommandModal } from 'twenty-sdk/command';
const DeleteDraft = () => {
const execute = async () => {
// perform the deletion
};
return (
<CommandModal
title="Delete draft?"
subtitle="This action cannot be undone."
execute={execute}
confirmButtonText="Delete"
confirmButtonAccent="danger"
/>
);
};
export default defineFrontComponent({
universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456',
name: 'delete-draft',
description: 'Deletes a draft with confirmation',
component: DeleteDraft,
isHeadless: true,
command: {
universalIdentifier: 'b8c9d0e1-f2a3-4567-bcde-678901234567',
label: 'Delete draft',
icon: 'IconTrash',
},
});
```
#### Accessing runtime context
Inside your component, use SDK hooks to access the current user, record, and component instance:
@@ -859,6 +953,50 @@ Front components can trigger navigation, modals, and notifications using functio
| `unmountFrontComponent()` | Unmount the component |
| `updateProgress(progress)` | Update a progress indicator |
Here is an example that uses the host API to show a snackbar and close the side panel after an action completes:
```tsx src/front-components/archive-record.tsx
import { defineFrontComponent, useRecordId } from 'twenty-sdk';
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk';
import { CoreApiClient } from 'twenty-sdk/clients';
const ArchiveRecord = () => {
const recordId = useRecordId();
const handleArchive = async () => {
const client = new CoreApiClient();
await client.mutation({
updateTask: {
__args: { id: recordId, data: { status: 'ARCHIVED' } },
id: true,
},
});
await enqueueSnackbar({
message: 'Record archived',
variant: 'success',
});
await closeSidePanel();
};
return (
<div style={{ padding: '20px' }}>
<p>Archive this record?</p>
<button onClick={handleArchive}>Archive</button>
</div>
);
};
export default defineFrontComponent({
universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678',
name: 'archive-record',
description: 'Archives the current record',
component: ArchiveRecord,
});
```
#### Command options
Adding a `command` field to `defineFrontComponent` registers the component in the command menu (Cmd+K). If `isPinned` is `true`, it also appears as a quick-action button in the top-right corner of the page.
@@ -1714,3 +1852,86 @@ yarn twenty uninstall
# Skip the confirmation prompt
yarn twenty uninstall --yes
```
## Managing remotes
A **remote** is a Twenty server that your app connects to. During setup, the scaffolder creates one for you automatically. You can add more remotes or switch between them at any time.
```bash filename="Terminal"
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote add
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote add --local
# Add a remote non-interactively (useful for CI)
yarn twenty remote add --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
# List all configured remotes
yarn twenty remote list
# Switch the active remote
yarn twenty remote switch <name>
```
Your credentials are stored in `~/.twenty/config.json`.
## CI with GitHub Actions
The scaffolder generates a ready-to-use GitHub Actions workflow at `.github/workflows/ci.yml`. It runs your integration tests automatically on every push to `main` and on pull requests.
The workflow:
1. Checks out your code
2. Spins up a temporary Twenty server using the `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` action
3. Installs dependencies with `yarn install --immutable`
4. Runs `yarn test` with `TWENTY_API_URL` and `TWENTY_API_KEY` injected from the action outputs
```yaml .github/workflows/ci.yml
name: CI
on:
push:
branches:
- main
pull_request: {}
env:
TWENTY_VERSION: latest
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Spawn Twenty instance
id: twenty
uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
with:
twenty-version: ${{ env.TWENTY_VERSION }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Enable Corepack
run: corepack enable
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn install --immutable
- name: Run integration tests
run: yarn test
env:
TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
```
You don't need to configure any secrets — the `spawn-twenty-docker-image` action starts an ephemeral Twenty server directly in the runner and outputs the connection details. The `GITHUB_TOKEN` secret is provided automatically by GitHub.
To pin a specific Twenty version instead of `latest`, change the `TWENTY_VERSION` environment variable at the top of the workflow.
@@ -7,7 +7,9 @@ description: Create your first Twenty app in minutes.
Apps are currently in alpha. The feature works but is still evolving.
</Warning>
Apps let you extend Twenty with custom objects, fields, logic functions, AI skills, and UI components — all managed as code.
## What are apps?
Apps let you extend Twenty with custom objects, fields, logic functions, front components, AI skills, and more — all managed as code. Instead of configuring everything through the UI, you define your data model and logic in TypeScript and deploy it to one or more workspaces.
## Prerequisites
@@ -17,7 +19,9 @@ Before you begin, make sure the following is installed on your machine:
- **Yarn 4** — Comes with Node.js via Corepack. Enable it by running `corepack enable`
- **Docker** — [Download here](https://www.docker.com/products/docker-desktop/). Required to run a local Twenty instance. Not needed if you already have a Twenty server running.
## Step 1: Scaffold your app
## Create your first app
### Scaffold your app
Open a terminal and run:
@@ -29,18 +33,7 @@ You will be prompted to enter a name and a description for your app. Press **Ent
This creates a new folder called `my-twenty-app` with everything you need.
<Note>
The scaffolder supports these flags:
- `--minimal` — scaffold only the essential files, no examples (default)
- `--exhaustive` — scaffold all example entities
- `--name <name>` — set the app name (skips the prompt)
- `--display-name <displayName>` — set the display name (skips the prompt)
- `--description <description>` — set the description (skips the prompt)
- `--skip-local-instance` — skip the local server setup prompt
</Note>
## Step 2: Set up a local Twenty instance
### Set up a local Twenty instance
The scaffolder will ask:
@@ -53,7 +46,7 @@ The scaffolder will ask:
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Should start local instance?" />
</div>
## Step 3: Sign in to your workspace
### Sign in to your workspace
Next, a browser window will open with the Twenty login page. Sign in with the pre-seeded demo account:
@@ -64,7 +57,7 @@ Next, a browser window will open with the Twenty login page. Sign in with the pr
<img src="/images/docs/developers/extends/apps/login.png" alt="Twenty login screen" />
</div>
## Step 4: Authorize the app
### Authorize the app
After you sign in, you will see an authorization screen. This lets your app interact with your workspace.
@@ -80,7 +73,7 @@ Once authorized, your terminal will confirm that everything is set up.
<img src="/images/docs/developers/extends/apps/scaffolded.png" alt="App scaffolded successfully" />
</div>
## Step 5: Start developing
### Start developing
Go into your new app folder and start the development server:
@@ -105,7 +98,7 @@ Dev mode is only available on Twenty instances running in development (`NODE_ENV
<img src="/images/docs/developers/extends/apps/dev.jpg" alt="Dev mode terminal output" />
</div>
## Step 6: See your app in Twenty
### See your app in Twenty
Open [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer) in your browser. Navigate to **Settings > Apps** and select the **Developer** tab. You should see your app listed under **Your Apps**:
@@ -133,13 +126,28 @@ Switch to the **Content** tab to see everything your app provides — objects, f
You are all set! Edit any file in `src/` and the changes will be picked up automatically.
Head over to [Building Apps](/developers/extend/apps/building) for a detailed guide on creating objects, logic functions, front components, skills, and more.
---
## What you can build
Apps are composed of **entities** — each defined as a TypeScript file with a single `export default`:
| Entity | What it does |
|--------|-------------|
| **Objects & Fields** | Define custom data models (like Post Card, Invoice) with typed fields |
| **Logic functions** | Server-side TypeScript functions triggered by HTTP routes, cron schedules, or database events |
| **Front components** | React components that render inside Twenty's UI (side panel, widgets, command menu) |
| **Skills & Agents** | AI capabilities — reusable instructions and autonomous assistants |
| **Views & Navigation** | Pre-configured list views and sidebar menu items for your objects |
| **Page layouts** | Custom record detail pages with tabs and widgets |
Head over to [Building Apps](/developers/extend/apps/building) for a detailed guide on each entity type.
---
## Project structure
The scaffolder generates the following file structure (shown with `--exhaustive` mode, which includes examples for every entity type):
The scaffolder generates the following file structure:
```text filename="my-twenty-app/"
my-twenty-app/
@@ -148,49 +156,35 @@ my-twenty-app/
.gitignore
.nvmrc
.yarnrc.yml
.yarn/
install-state.gz
.oxlintrc.json
tsconfig.json
tsconfig.spec.json # TypeScript config for tests
vitest.config.ts # Vitest test runner configuration
tsconfig.spec.json # TypeScript config for tests
vitest.config.ts # Vitest test runner configuration
LLMS.md
README.md
.github/
└── workflows/
└── ci.yml # GitHub Actions CI workflow
public/ # Public assets (images, fonts, etc.)
└── ci.yml # GitHub Actions CI workflow
public/ # Public assets (images, fonts, etc.)
src/
├── application-config.ts # Required — main application configuration
├── __tests__/
├── setup-test.ts # Test setup (server health check, config)
│ └── app-install.integration-test.ts # Example integration test
── roles/
── default-role.ts # Default role for logic functions
├── objects/
│ └── example-object.ts # Example custom object definition
├── fields/
│ └── example-field.ts # Example standalone field definition
├── logic-functions/
│ ├── hello-world.ts # Example logic function
│ ├── create-hello-world-company.ts # Example logic function using CoreApiClient
│ ├── pre-install.ts # Runs before installation
│ └── post-install.ts # Runs after installation
├── front-components/
│ └── hello-world.tsx # Example front component
├── page-layouts/
│ └── example-record-page-layout.ts # Example page layout with front component
├── views/
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
├── skills/
│ └── example-skill.ts # Example AI agent skill definition
└── agents/
└── example-agent.ts # Example AI agent definition
├── application-config.ts # Required — main application configuration
├── default-role.ts # Default role for logic functions
├── constants/
│ └── universal-identifiers.ts # Auto-generated UUIDs and app metadata
── __tests__/
── setup-test.ts # Test setup (server health check, config)
└── app-install.integration-test.ts # Integration test
```
By default (`--minimal`), only the core files are created: `application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts`, and `logic-functions/post-install.ts`. Use `--exhaustive` to include all the example files shown above.
### Starting from an example
To start from a more complete example with custom objects, fields, logic functions, front components, and more, use the `--example` flag:
```bash filename="Terminal"
npx create-twenty-app@latest my-twenty-app --example postcard
```
Examples are sourced from the [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples) directory on GitHub. You can also scaffold individual entities into an existing project with `yarn twenty add` (see [Building Apps](/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add)).
### Key files
@@ -198,109 +192,14 @@ By default (`--minimal`), only the core files are created: `application-config.t
|---|---|
| `package.json` | Declares your app name, version, and dependencies. Includes a `twenty` script so you can run `yarn twenty help` to see all commands. |
| `src/application-config.ts` | **Required.** The main configuration file for your app. |
| `src/roles/` | Defines roles that control what your logic functions can access. |
| `src/logic-functions/` | Server-side functions triggered by routes, cron schedules, or database events. |
| `src/front-components/` | React components that render inside Twenty's UI. |
| `src/objects/` | Custom object definitions to extend your data model. |
| `src/fields/` | Custom fields added to existing objects. |
| `src/views/` | Saved view configurations. |
| `src/navigation-menu-items/` | Custom links in the sidebar navigation. |
| `src/skills/` | Skills that extend Twenty's AI agents. |
| `src/agents/` | AI agents with custom prompts. |
| `src/page-layouts/` | Custom page layouts for record views. |
| `src/default-role.ts` | Default role that controls what your logic functions can access. |
| `src/constants/universal-identifiers.ts` | Auto-generated UUIDs and app metadata (display name, description). |
| `src/__tests__/` | Integration tests (setup + example test). |
| `public/` | Static assets (images, fonts) served with your app. |
## Managing remotes
## Local development server
A **remote** is a Twenty server that your app connects to. During setup, the scaffolder creates one for you automatically. You can add more remotes or switch between them at any time.
```bash filename="Terminal"
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote add
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote add --local
# Add a remote non-interactively (useful for CI)
yarn twenty remote add --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
# List all configured remotes
yarn twenty remote list
# Switch the active remote
yarn twenty remote switch <name>
```
Your credentials are stored in `~/.twenty/config.json`.
## Local development server (`yarn twenty server`)
The CLI can manage a local Twenty server running in Docker. This is the same server started automatically when you scaffold an app with `create-twenty-app`, but you can also manage it manually.
### Starting the server
```bash filename="Terminal"
yarn twenty server start
```
This pulls the `twentycrm/twenty-app-dev:latest` Docker image (if not already present), creates a container named `twenty-app-dev`, and starts it on port **2020**. The CLI waits until the server passes its health check before returning.
Two Docker volumes are created to persist data between restarts:
- `twenty-app-dev-data` — PostgreSQL database
- `twenty-app-dev-storage` — file storage
If port 2020 is already in use, you can start on a different port:
```bash filename="Terminal"
yarn twenty server start --port 3030
```
The CLI automatically configures the container's internal `NODE_PORT` and `SERVER_URL` to match the chosen port, so logic functions, OAuth, and all other internal networking work correctly.
Once started, the server is automatically registered as the `local` remote in your CLI config.
### Checking server status
```bash filename="Terminal"
yarn twenty server status
```
Displays whether the server is running, its URL, and the default login credentials (`tim@apple.dev` / `tim@apple.dev`).
### Viewing server logs
```bash filename="Terminal"
yarn twenty server logs
```
Streams the container logs. Use `--lines` to control how many recent lines to show:
```bash filename="Terminal"
yarn twenty server logs --lines 100
```
### Stopping the server
```bash filename="Terminal"
yarn twenty server stop
```
Stops the container. Your data is preserved in the Docker volumes — the next `start` picks up where you left off.
### Resetting the server
```bash filename="Terminal"
yarn twenty server reset
```
Removes the container **and** deletes both Docker volumes, wiping all data. The next `start` creates a fresh instance.
<Note>
The server requires **Docker** to be running. If you see a "Docker not running" error, make sure Docker Desktop (or the Docker daemon) is started.
</Note>
### Command reference
The scaffolder already started a local Twenty server for you. To manage it later, use `yarn twenty server`:
| Command | Description |
|---------|-------------|
@@ -312,66 +211,11 @@ Removes the container **and** deletes both Docker volumes, wiping all data. The
| `yarn twenty server logs --lines 100` | Show the last 100 log lines |
| `yarn twenty server reset` | Delete all data and start fresh |
Data is persisted across restarts in two Docker volumes (`twenty-app-dev-data` for PostgreSQL, `twenty-app-dev-storage` for files). Use `reset` to wipe everything and start fresh.
## CI with GitHub Actions
The scaffolder generates a ready-to-use GitHub Actions workflow at `.github/workflows/ci.yml`. It runs your integration tests automatically on every push to `main` and on pull requests.
The workflow:
1. Checks out your code
2. Spins up a temporary Twenty server using the `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` action
3. Installs dependencies with `yarn install --immutable`
4. Runs `yarn test` with `TWENTY_API_URL` and `TWENTY_API_KEY` injected from the action outputs
```yaml .github/workflows/ci.yml
name: CI
on:
push:
branches:
- main
pull_request: {}
env:
TWENTY_VERSION: latest
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Spawn Twenty instance
id: twenty
uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
with:
twenty-version: ${{ env.TWENTY_VERSION }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Enable Corepack
run: corepack enable
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn install --immutable
- name: Run integration tests
run: yarn test
env:
TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
```
You don't need to configure any secrets — the `spawn-twenty-docker-image` action starts an ephemeral Twenty server directly in the runner and outputs the connection details. The `GITHUB_TOKEN` secret is provided automatically by GitHub.
To pin a specific Twenty version instead of `latest`, change the `TWENTY_VERSION` environment variable at the top of the workflow.
<Note>
The server requires **Docker** to be running. If you see a "Docker not running" error, make sure Docker Desktop (or the Docker daemon) is started.
</Note>
## Manual setup (without the scaffolder)
@@ -1,81 +0,0 @@
---
title: Twenty Apps
description: Build and manage Twenty customizations as code.
---
<Warning>
Apps are currently in alpha. The feature works but is still evolving.
</Warning>
## What are apps?
Apps let you extend Twenty with custom objects, fields, logic functions, front components, AI skills, and more — all managed as code. Instead of configuring everything through the UI, you define your data model and logic in TypeScript and deploy it to one or more workspaces.
**What you can build:**
- **Custom objects and fields** — extend your data model with new entities or add fields to existing objects like Company or Person
- **Logic functions** — server-side functions triggered by database events, cron schedules, or HTTP routes
- **Front components** — React components that render inside Twenty's UI (record pages, command menu, side panels)
- **AI skills and agents** — extend Twenty's AI with custom capabilities
- **Views and navigation** — preconfigured saved views and sidebar links
## Quick start
```bash filename="Terminal"
npx create-twenty-app@latest my-twenty-app
cd my-twenty-app
yarn twenty dev
```
This scaffolds a new app, optionally starts a local Twenty server, and begins watching your files for changes. See the [Getting Started](/developers/extend/apps/getting-started) guide for the full walkthrough.
## Detailed guides
| Guide | Description |
|-------|-------------|
| [Getting Started](/developers/extend/apps/getting-started) | Scaffold an app, set up a local server, project structure, CI |
| [Building Apps](/developers/extend/apps/building) | Entity definitions (`defineObject`, `defineLogicFunction`, `defineFrontComponent`, etc.), API clients, npm packages, public assets, testing |
| [Publishing](/developers/extend/apps/publishing) | Deploy to a server, publish to npm, marketplace |
## Key concepts
### Entity detection
The SDK detects entities by scanning your TypeScript files for `export default define<Entity>({...})` calls. File naming and folder structure are flexible — detection is AST-based, not path-based.
### Available entity types
| Function | Purpose |
|----------|---------|
| `defineApplication()` | Application metadata (required, one per app) |
| `defineObject()` | Custom objects with fields |
| `defineField()` | Fields on existing objects |
| `defineLogicFunction()` | Server-side logic with triggers |
| `defineFrontComponent()` | React components in Twenty's UI |
| `defineRole()` | Permission roles |
| `defineView()` | Saved view configurations |
| `defineNavigationMenuItem()` | Sidebar navigation links |
| `defineSkill()` | AI agent skills |
| `defineAgent()` | AI agents with prompts |
| `definePageLayout()` | Custom record page layouts |
| `definePreInstallLogicFunction()` | Runs before app installation |
| `definePostInstallLogicFunction()` | Runs after app installation |
### Development workflow
1. **`yarn twenty dev`** — watches source files, rebuilds on change, syncs to the server, generates typed API clients
2. **`yarn twenty build`** — produces a distributable build
3. **`yarn twenty deploy`** — deploys to a remote Twenty server
4. **`yarn twenty add`** — scaffolds a new entity interactively
### CLI reference
```bash filename="Terminal"
yarn twenty help # List all commands
yarn twenty server start # Start local dev server
yarn twenty remote add # Connect to a Twenty server
yarn twenty exec -n fn # Execute a logic function
yarn twenty logs -n fn # Stream function logs
```
See the [Getting Started](/developers/extend/apps/getting-started) guide for the full CLI reference.