Skip to content

Anatomy of an extension

Every extension made with the SDK follows a standard file structure and includes a set of core files that make the extension work on Netlify. This page provides an introduction to the anatomy of an extension, including additional details for the optional functionality you might add.

Showcase extension

For a complete example of an extension and all of the files that make it work, refer to the Netlify extension showcase repository.

Extension file structure

Depending on which boilerplate options you select during the guided set-up flow, the SDK will generate all or a subset of the following files for you:

├── details.md
├── extension.yaml
├── netlify.toml
├── package.json
├── src
│ ├── edge-functions
│ │ └── hello-world.ts
│ ├── endpoints
│ │ └── trpc.ts
│ ├── functions
│ │ └── hello-world.mts
│ ├── index.ts
│ ├── schema
│ │ └── team-configuration.ts
│ ├── server
│ │ ├── router.ts
│ │ └── trpc.ts
│ └── ui
│ ├── App.tsx
│ ├── index.css
│ ├── index.html
│ ├── index.tsx
│ ├── surfaces
│ │ ├── ConnectConfiguration.tsx
│ │ ├── SiteConfiguration.tsx
│ │ ├── TeamConfiguration.tsx
│ │ └── VisualEditorConfiguration.tsx
│ └── trpc.ts

Endpoints

While there isn’t a stand-alone boilerplate option for Endpoints, the SDK will automatically generate the /endpoints directory for you when you select Build Event Handler or Extension UI. Alternatively, you can create the directory and save your endpoint files in it manually. Learn more about endpoints.

Core extension files

Every extension includes the following required files by default, regardless of which boilerplate option you choose:

FileSummary
src/index.tsThe entrypoint for developing the extension. This is where you create, configure, and export your extension.
extension.yamlThe extension configuration file. This is where you configure extension metadata and declare extension surfaces for your extension UI.
netlify.tomlThe Netlify configuration file for the site that will host your extension. This is where you configure the settings for building and deploying your extension.
package.jsonThe standard project file that contains metadata about your extension, the path to the entry file, dependencies, scripts, and more.
details.mdThe markdown file that Netlify renders on the extension’s details page in the Netlify UI after you publish the extension. This is where you add documentation for your users.

All extensions need UI

When you create an extension, we recommend that you select the Extension UI boilerplate at a minimum because all extensions need to define UI for users to configure their extension. Learn more about extension UI files in the section below.

src/index.ts

The Netlify SDK automatically generates a src/index.ts file and specifies it as the main entry file in the package.json. The SDK uses this file as the entry point to load and bundle your extension.

If you update the filename, you must ensure that you update the main field of the package.json. For example, the filename may be src/entry.ts, and the package.json would include it as "main": "src/entry.ts".

The main entry file is where you include all of the code that configures your extension and related functionality. The file must export a valid NetlifyExtension object.

src/index.ts
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
// The code to configure your extension and its functionality goes here
export { extension };

extension.yaml

The extension.yaml is the manifest file for your extension and it includes the following key values:

  • slug: the slug for your extension
  • name: the name of your extension
  • scopes: the required permission scopes based on the boilerplate you selected
  • ui: a list of the surfaces that your extension UI contains
extension.yaml
config:
slug: example-extension
name: example
scopes:
site: ["read", "write"]
user: ["read", "write"]
env: ["read", "write"]
use_cjs_shims: false
ui:
surfaces:
- extension-team-configuration
- extension-top-level-site-configuration

netlify.toml

One of the first steps in the publishing process is to deploy your extension as a site on Netlify.

The netlify.toml is the Netlify configuration file that you use to configure the settings for building and deploying your extension on Netlify.

netlify.toml
[build]
command = "netlify-extension build"
publish = ".ntli/site/static"
[dev]
command = "netlify-extension dev"
[functions]
directory = "src/endpoints"
[[plugins]]
package = "@netlify/netlify-plugin-netlify-extension"

package.json

The automatically generated package.json includes the following required fields:

It also automatically includes the @netlify/sdk dependency, along with related scripts for building and developing your extension.

package.json
{
"name": "example",
"version": "0.0.1",
"dependencies": {
"@netlify/sdk": "^2.8.0"
},
"main": "src/index.ts",
"type": "module",
"scripts": {
"build": "netlify-extension build -a",
"dev": "netlify-extension dev --open"
},
}

details.md

The details.md is a markdown file that you can use to add documentation for users on how to configure and use your extension. After you publish your extension, Netlify will render this markdown file on your extension’s details page in the Netlify UI.

details.md
# My example extension
This is the documentation for my example extension.
## Install and configure the extension
To use this extension...

Learn more about how to add documentation for your extension.

Extension UI files

When you select the Extension UI boilerplate, the SDK generates the following extension UI and endpoint directories, with example files within them.

Note that the boilerplate comes with the tRPC integration configured for you. You can use the integration to write procedures and call them from your extension UI.

FileSummary
src/endpoints/The folder to store the endpoint files for your extension. The trpc.ts file initializes an endpoint that serves the tRPC router.
server/The folder that contains the router.ts and trpc.ts files for the tRPC router and procedures.
src/ui/The folder to store all of the related files for your extension’s UI, including the index.tsx, App.tsx, and your surfaces under /surfaces/

The SDK will also install the required modules and generate the related configuration files for each tool and library in the extension UI’s technical stack:

Tech stack

Learn more about each of these files in get started with extension UI and call endpoints from extension UI.

Build event handler files

When you select the Build Event Handler boilerplate, the SDK includes the configuration for an example build event handler in src/index.ts.

For example
src/index.ts
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", () => {
// If the build event handler is not enabled, return early
if (!process.env["MY_EXAMPLE_HANDLER_ENABLED"]) {
return;
}
console.log("Hello there.");
});
export { extension };

The SDK also generates extension UI files required for a user to configure a build event handler.

Learn more in get started with build event handlers.

Connector files

When you select the Connector boilerplate, the SDK includes the configuration for an example connector in src/index.ts.

For example
src/index.ts
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
const connector = extension.addConnector({
typePrefix: "Example",
supports: {
connect: true,
visualEditor: false,
},
});
connector.model(async ({ define }) => {
define.document({
name: "User",
fields: {
name: {
type: "String",
required: true,
},
posts: {
type: "Post",
list: true,
},
},
});
define.document({
name: "Post",
fields: {
title: {
type: "String",
required: true,
},
blocks: {
list: true,
required: true,
type: define.object({
name: "Blocks",
fields: {
title: {
type: "String",
},
content: {
type: "String",
},
},
}),
},
},
});
});
connector.sync(({ models, isInitialSync }) => {
switch (true) {
case isInitialSync: {
models.User.insert({
id: "1",
name: "Annie",
posts: [
{
id: "1",
__typename: "Post",
},
],
_status: "published",
_createdAt: new Date(),
});
models.Post.insert({
id: "1",
title: "Hello World",
blocks: [
{
title: "Example block title",
content: "You can create complex content models",
},
],
_status: "published",
_createdAt: new Date(),
});
break;
}
case !isInitialSync: {
models.User.insert({
id: "1",
name: "Annie",
posts: [
{
id: "1",
__typename: "Post",
},
],
_status: "published",
_createdAt: new Date(),
});
models.Post.insert({
id: "2",
title: "Writing lots of posts these days",
blocks: [
{
title: "Page section",
content: "what up",
},
],
_status: "published",
_createdAt: new Date(),
});
break;
}
}
});
export { extension };

Learn more in get started with connectors.

Edge function injection files

When you select the Edge Function Injection boilerplate, the SDK generates the following edge functions directory and example file within it.

FileSummary
src/edge-functions/The folder to store edge functions in, with a sample edge function in hello-world.ts.

Learn more in get started with edge function injection.

Function injection files

When you select the Functions Injection boilerplate, the SDK generates the following functions directory and example file within it.

FileSummary
src/functions/The folder to store serverless functions in, with a sample function in hello-world.mts.

Learn more in get started with function injection.

More resources

Got it!

Your feedback helps us improve our docs.