webiny-project-structure

Webiny project layout, webiny.config.tsx anatomy, and extension registration. Use this skill when the developer asks about folder structure, where custom code goes, how to register extensions, what webiny.config.tsx does, or how the project is organized. Also use when they need to understand the relationship between extensions/, webiny.config.tsx, and the different extension types (Api, Admin, Infra, CLI).

7,955 stars

Best use case

webiny-project-structure is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Webiny project layout, webiny.config.tsx anatomy, and extension registration. Use this skill when the developer asks about folder structure, where custom code goes, how to register extensions, what webiny.config.tsx does, or how the project is organized. Also use when they need to understand the relationship between extensions/, webiny.config.tsx, and the different extension types (Api, Admin, Infra, CLI).

Teams using webiny-project-structure should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/project-structure/SKILL.md --create-dirs "https://raw.githubusercontent.com/webiny/webiny-js/main/skills/user-skills/project-structure/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/project-structure/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How webiny-project-structure Compares

Feature / Agentwebiny-project-structureStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Webiny project layout, webiny.config.tsx anatomy, and extension registration. Use this skill when the developer asks about folder structure, where custom code goes, how to register extensions, what webiny.config.tsx does, or how the project is organized. Also use when they need to understand the relationship between extensions/, webiny.config.tsx, and the different extension types (Api, Admin, Infra, CLI).

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# Webiny Project Structure

## TL;DR

A Webiny project has a flat structure centered around `webiny.config.tsx` -- the single configuration file where all extensions are registered. Custom code lives in the `extensions/` folder. Extensions are registered as React components (`<Api.Extension>`, `<Admin.Extension>`, `<Infra.*>`, `<Cli.Command>`) and can be conditionally loaded per environment.

## Project Layout

```
my-webiny-project/
├── extensions/          # All custom code -- API, Admin, Infra, CLI extensions
│   └── README.md
├── public/              # Static assets for the Admin app
│   ├── favicon.ico
│   ├── global.css
│   ├── index.html
│   └── robots.txt
├── eslint.config.js     # ESLint configuration
├── package.json         # Single package.json for the whole project
├── tsconfig.json        # Single TypeScript config
├── webiny.config.tsx    # Main configuration -- all extensions registered here
├── webiny-env.d.ts      # TypeScript environment types
└── yarn.lock
```

Key points:

- **Single `package.json`** -- no monorepo, no workspaces needed.
- **Single `tsconfig.json`** -- straightforward TypeScript setup.
- **`webiny.config.tsx`** -- the entry point for everything. All extensions, infrastructure options, and project settings are declared here.
- **`extensions/`** -- where all your custom code lives. Organize with subfolders as needed (e.g., `extensions/contactSubmission/`, `extensions/AdminBranding/`).

## The `webiny.config.tsx` File

This file exports a single React component called `Extensions`. It uses JSX to declaratively register all configuration:

```tsx
// webiny.config.tsx
import React from "react";
import { Admin, Api, Cli, Infra, Project, Security } from "webiny/extensions";
import { Cognito } from "@webiny/cognito";

export const Extensions = () => {
  return (
    <>
      {/* Infrastructure configuration */}
      <Infra.Aws.DefaultRegion name={"us-east-1"} />
      <Infra.OpenSearch enabled={true} />
      <Infra.Aws.Tags tags={{ OWNER: "me", PROJECT: "my-project" }} />

      {/* Identity provider */}
      <Cognito />

      {/* API extensions (backend) */}
      <Api.Extension src={"/extensions/ProductCategoryModel.ts"} />
      <Api.Extension src={"/extensions/ProductModel.ts"} />
      <Api.Extension src={"/extensions/contactSubmission/ContactSubmissionHook.ts"} />

      {/* Security hooks */}
      <Api.Extension src={"/extensions/MyApiKey.ts"} />
      <Security.ApiKey.AfterUpdate src={"/extensions/MyApiKeyAfterUpdate.ts"} />

      {/* Admin extensions (frontend) */}
      <Admin.Extension src={"/extensions/AdminTheme/AdminTheme.tsx"} />
      <Admin.Extension src={"/extensions/AdminTitleLogo/AdminTitleLogo.tsx"} />
      <Admin.Extension src={"/extensions/contactSubmission/EmailEntryListColumn.tsx"} />

      {/* Infrastructure / Pulumi extensions */}
      <Infra.Core.Pulumi src={"/extensions/MyCorePulumiHandler.ts"} />

      {/* CLI extensions */}
      <Cli.Command src={"/extensions/MyCustomCommand.ts"} />

      {/* Project settings */}
      <Project.Telemetry enabled={false} />
    </>
  );
};
```

## Extension Types

| JSX Element                                 | What It Does                                                                             | File Type |
| ------------------------------------------- | ---------------------------------------------------------------------------------------- | --------- |
| `<Api.Extension src="..." />`               | Registers a backend extension (GraphQL schemas, content models, lifecycle hooks)         | `.ts`     |
| `<Admin.Extension src="..." />`             | Registers a frontend Admin UI extension (themes, branding, custom columns, custom forms) | `.tsx`    |
| `<Infra.Core.Pulumi src="..." />`           | Registers a Pulumi infrastructure handler                                                | `.ts`     |
| `<Cli.Command src="..." />`                 | Registers a custom CLI command                                                           | `.ts`     |
| `<Security.ApiKey.AfterUpdate src="..." />` | Registers a security lifecycle hook                                                      | `.ts`     |

**YOU MUST include the full file path with the `.ts` or `.tsx` extension in every `src` prop.** For example, use `src={"/extensions/MyModel.ts"}`, NOT `src={"/extensions/MyModel"}`. Omitting the file extension will cause a build failure.

**YOU MUST use `export default` for the `createImplementation()` call** when the file is targeted directly by an Extension `src` prop. Using a named export (`export const Foo = SomeFactory.createImplementation(...)`) will cause a build failure. Named exports are only valid inside files registered via `createFeature`.

## Infrastructure Components

Declarative components for configuring AWS infrastructure:

| Component                                                             | Purpose                                |
| --------------------------------------------------------------------- | -------------------------------------- |
| `<Infra.Aws.DefaultRegion name="us-east-1" />`                        | Set the AWS region                     |
| `<Infra.Aws.Tags tags={{ KEY: "value" }} />`                          | Apply tags to all AWS resources        |
| `<Infra.OpenSearch enabled={true} />`                                 | Enable/disable OpenSearch              |
| `<Infra.Vpc enabled={true} />`                                        | Enable/disable VPC deployment          |
| `<Infra.PulumiResourceNamePrefix prefix="myproj-" />`                 | Prefix all Pulumi resource names       |
| `<Infra.ProductionEnvironments environments={["prod", "staging"]} />` | Define which envs use production infra |
| `<Project.Telemetry enabled={false} />`                               | Enable/disable telemetry               |

## Environment-Conditional Configuration

Use `<Infra.Env.Is>` to load extensions or config only in specific environments:

```tsx
<Infra.Env.Is env="prod">
    <Infra.Aws.Tags tags={{ ENV: "production" }} />
    <Infra.OpenSearch enabled={true} />
</Infra.Env.Is>

<Infra.Env.Is env={["dev", "staging"]}>
    <Infra.Aws.Tags tags={{ ENV: "non-production" }} />
    <Infra.OpenSearch enabled={false} />
</Infra.Env.Is>
```

## Build Parameters

Build parameters pass values from config to extensions at build time. They are accessed in backend code via the `BuildParams` DI service (see dependency-injection skill).

**Rule: `<Api.BuildParam>` and `<Admin.BuildParam>` MUST live inside the extension's `Extension.tsx`, NOT directly in `webiny.config.tsx`.** Required parameters are exposed as React props on the extension component. The consumer in `webiny.config.tsx` decides where the value comes from (env var, hardcoded, config, etc.).

```tsx
// extensions/myExtension/Extension.tsx
import React from "react";
import { Api } from "webiny/extensions";

interface MyExtensionProps {
  apiKey: string;
}

export const MyExtension = ({ apiKey }: MyExtensionProps) => {
  return (
    <>
      <Api.BuildParam paramName="MY_API_KEY" value={apiKey} />
      <Api.Extension src={"@/extensions/myExtension/features/myService/feature.ts"} />
    </>
  );
};
```

```tsx
// webiny.config.tsx — consumer decides where the value comes from
<MyExtension apiKey={process.env.MY_API_KEY || ""} />
```

This keeps extensions self-contained — all required configuration is declared via typed props in one place.

## Installing Pre-Built Extensions

Webiny provides official extensions you can install with:

```bash
yarn webiny extension <extension-name>
```

This downloads the extension code into `extensions/`, updates `webiny.config.tsx` to register it, and gives you full access to modify the code.

## Related Skills

- `webiny-dependency-injection` -- How extensions use DI to access services
- `webiny-local-development` -- How to deploy and develop locally
- `webiny-full-stack-architect` -- Full-stack extension skeleton, entry points, and shared domain layer

Related Skills

webiny-v5-to-v6-migration

7955
from webiny/webiny-js

Migration patterns for converting v5 Webiny code to v6 architecture. Use this skill when migrating existing v5 plugins to v6 features, converting context plugins to DI services, adapting v5 event subscriptions to v6 EventHandlers, or understanding how v5 patterns translate to v6. Targeted at AI agents performing migrations.

webiny-api-permissions

7955
from webiny/webiny-js

Schema-based permission system for API features. Use this skill when implementing authorization in use cases, defining permission schemas with createPermissionSchema, creating injectable permissions via createPermissionsAbstraction/createPermissionsFeature, checking read/write/delete/publish permissions, handling own-record scoping, or testing permission scenarios. Covers the full pattern from schema definition to use case integration to test matrices.

webiny-admin-permissions

7955
from webiny/webiny-js

Admin-side permission UI registration and DI-backed permission checking. Use this skill when adding permission controls to the admin UI — schema-based auto-generated forms, injectable permissions via createPermissionsAbstraction/ createPermissionsFeature, typed hooks (createUsePermissions), the HasPermission component (createHasPermission), and the Security.Permissions component props. Covers both simple apps and complex multi-entity permission schemas.

webiny-sdk

7955
from webiny/webiny-js

Using @webiny/sdk to read and write CMS data from external applications. Use this skill when the developer is building a Next.js, Vue, Node.js, or any external app that needs to fetch or write content to Webiny, set up the SDK, use the Result pattern, list/get/create/update/publish entries, filter and sort queries, use TypeScript generics for type safety, work with the File Manager, or create API keys programmatically. Covers read vs preview mode, the `values` wrapper requirement, correct method names, and the `fields` required parameter.

webiny-local-development

7955
from webiny/webiny-js

Deploying, developing locally, managing environments, and debugging Webiny projects. Use this skill when the developer asks about deployment commands (deploy, destroy, info), local development with watch mode (API or Admin), the Local Lambda Development system, environment management (long-lived vs short-lived, production vs dev modes), build parameters, state files, debugging API/Admin/Infrastructure errors, or the redeploy-after-watch requirement.

webiny-infrastructure-extensions

7955
from webiny/webiny-js

Modifying AWS infrastructure using Pulumi handlers and declarative Infra components. Use this skill when the developer wants to customize AWS infrastructure, add Pulumi handlers, configure OpenSearch, VPC, resource tags, regions, custom domains, blue-green deployments, environment-conditional config, or manage production vs development infrastructure modes. Covers CorePulumi.Interface, all <Infra.*> declarative components, and <Infra.Env.Is>.

webiny-infra-catalog

7955
from webiny/webiny-js

Infrastructure — 33 abstractions. Infrastructure extensions.

webiny-extensions-catalog

7955
from webiny/webiny-js

extensions — 5 abstractions.

webiny-cli-command-catalog

7955
from webiny/webiny-js

cli/command — 1 abstractions.

webiny-cli-catalog

7955
from webiny/webiny-js

cli — 2 abstractions.

webiny-api-tenant-manager-catalog

7955
from webiny/webiny-js

API — Tenant Manager — 2 abstractions. Tenant management event handlers and use cases.

webiny-api-tasks-catalog

7955
from webiny/webiny-js

api/tasks — 2 abstractions.