# Import

> Turn a repository, a template, or a local folder into a project the platform builds.

Canonical: https://docs.extension.dev/guides/import
This page as Markdown: https://docs.extension.dev/guides/import.md
Section: Ways to start a project

Map of every page: https://docs.extension.dev/llms.txt · Whole corpus: https://docs.extension.dev/llms-full.txt

---

# Import

Import turns a repository, a template, or a folder on your machine into a
project with builds. Reach for it when an extension already exists and you
want the platform building it.

## Quick start

```bash
cd my-extension
gh repo create my-extension --private --source=. --push
```

Then open [extension.dev/import](https://www.extension.dev/import), sign in
with GitHub, and pick the repository. The import analyzes the tree, fills the
configuration, creates the project, and queues the first build.


## How it works

### Three shapes, one page

Three kinds of input reach the same page. A repository arrives from the
picker behind [/new](https://www.extension.dev/new) or from a direct link, and
needs a GitHub session. A template arrives as `?template=<slug>` from the
[template catalog](/templates/). Its configuration works signed out, and
creating the repository needs a session. A local folder with no remote needs
one remote first, as the next section shows.

Signed in, `/new` sends you to the console, where an App installation
selector and a repository picker hand the chosen repository to `/import`.
Signed out, `/new` shows the sign-in and the template tiles.

### A folder on your machine, with no remote yet

`extension create` scaffolds the folder, runs `git init`, and commits the
template on the branch your git creates by default. What it does not do is
create a remote. `/import` reads repositories GitHub can serve, so one remote
is the whole distance.

```bash
cd my-extension
gh repo create my-extension --private --source=. --push
```

There is no `git init` step because the scaffold already ran it, and no
`git add` or `git commit` step because the scaffold already committed. On a
fresh scaffold those two answer "nothing to commit, working tree clean". Any
way of creating the remote works, and `gh` is the shortest. Then open
[extension.dev/import](https://www.extension.dev/import) and pick the
repository.

At least one commit has to reach the remote. A source repository with no
commits is the one case with nothing to build, and the import says so instead
of starting a build.

If nobody needs the project on the platform yet and one person just needs to
look at the build, the upload path takes the bytes in `dist/` and asks for no
repository at all. See
[Share an unpublished build for review](/share/unpublished-build-for-review).
It still needs a token, and a token is minted against a project, so that path
is shorter only when a project already exists.

The framework side of this folder, `create`, `dev` and `build`, is documented
at [extension.js.org](https://extension.js.org). Extension.dev sponsors
Extension.js, the open source framework it builds on, and the platform half
starts at the commit above.

### What the analysis reads

The import calls `POST /api/import/analyze`, which walks the git tree and
fills the configuration for you:

- **`manifest.json`**, the one nearest the selected root directory. Its keys
  decide which browsers start enabled, and every enabled browser is one build
  per import.
- **Monorepos**: a nested `package.json` marks a subproject, and the analysis
  suggests that directory as the project root.
- **Build command and output directory**, inferred per browser. The output
  defaults are `dist/chrome`, `dist/edge`, and `dist/firefox`.

The browser rule is under Reference. A manifest that names Firefox and
carries ordinary keys enables all three browsers, and the import builds three
artifacts. Turn off the ones you do not ship under **Browser builds** before
you create the project.

Reading a public repository is the one step the GitHub App is not needed for.
Analyzing a private repository needs it, and so does anything that writes,
including creating a repository from a template. Install it on the account or
organization that owns the repository.

### What it does not need

The import does not require a specific framework, bundler, or directory
layout. It needs a manifest and a build that produces a loadable extension
directory. An extension that does not use Extension.js still imports.

### What the button does

One button runs the import. It reads **Create project** for a repository you
already own, and **Create repository and deploy** for a template or a
generation.

Template and generation imports create the repository first, so they need the
GitHub App installed on the account or organization that will own it. That
step is `POST /api/templates/create-repo`.

Every import then posts the configuration to `POST /api/clone?stream=1` and
reads the progress back as a stream. That one call does the rest:

1. Checks that your GitHub session holds the installation you selected.
2. Creates the workspace, if you do not have one, and the project record.
3. Creates the build repository where builds run.
4. Writes the project settings, the release channels, and the project files.
5. Saves your environment variables as repository secrets.
6. Queues the first build.

The stream ends with a `done` event carrying the project id, and the page
sends you to the project in the console.

### After the import

The project exists, bound to its build repository, and the first build is
already queued. You do not need to push a commit to see it. Later builds come
from your pushes. Artifacts land under **Builds**, one per enabled browser.

## Reference

| Arriving with | Comes from | Sign-in |
| --- | --- | --- |
| A repository | The repository picker behind [/new](https://www.extension.dev/new), or a direct link | GitHub session required |
| `?template=<slug>` | The [template catalog](/templates/) | Configuration works signed out, creating the repository needs a session |
| A local folder with no remote | `extension create` on your machine | One remote first, [above](#a-folder-on-your-machine-with-no-remote-yet) |

| What the analysis reads | What it enables |
| --- | --- |
| Ordinary manifest keys, with no browser prefix | Chrome and Edge |
| `browser_specific_settings` or `applications` | Firefox |
| Keys prefixed `chrome:`, `chromium:` or `edge:` | Chrome and Edge |
| Keys prefixed `firefox:` or `gecko:` | Firefox |
| A `build` script in `package.json` | Chrome |
| A `build:edge` or `build:firefox` script, or that browser named inside the build script | That browser |
| Nothing it recognizes | Chrome alone |

| Limit | Value |
| --- | --- |
| Repository creations from a template, per user | 20 an hour |
| Imports, per user | 20 an hour |
| Projects per workspace | 1 on Free, 10 on Pro |
| Default output directories | `dist/chrome`, `dist/edge`, `dist/firefox` |

## When it fails

- **There is nothing to select, because the extension is only a folder.** A
  scaffolded folder has a git repository and a first commit, but no remote, so
  no repository picker can see it. Push it to GitHub, see
  [above](#a-folder-on-your-machine-with-no-remote-yet).
- **The analysis finds no manifest.** There is no `manifest.json` in the tree,
  or it lives outside the selected root directory. For monorepos, accept the
  suggested root or set it by hand.
- **A private repository will not analyze.** Install the GitHub App on the
  owner of the repository. Public repositories analyze without it.
- **Builds run but the artifact is empty.** The output directory does not
  match what the build writes. The defaults are `dist/chrome`, `dist/edge`,
  `dist/firefox`. Override them per browser in **Project settings, Browser
  builds**. An Extension.js build command with no `--browser` flag targets
  chromium and writes `dist/chromium`, which none of the defaults read, so
  the command must name the browser. The analysis infers `--browser=chrome`,
  `--browser=edge` and `--browser=firefox`.
- **The first build covers browsers you do not ship.** The analysis enabled
  them from the manifest. Turn them off under **Project settings, Browser
  builds**, and turn them off before the import next time.
- **The import is refused because the workspace is full.** The plan caps how
  many projects a workspace holds, 1 on Free and 10 on Pro, and the message
  names the number and what you already have. Your existing projects keep
  working. Re-importing a repository the workspace already holds is the one
  exception: it reuses that project instead of being refused. See
  [extension.dev/pricing](https://www.extension.dev/pricing).
- **Repository creation is refused with a rate limit.** Template imports
  create at most 20 repositories an hour per user, and every import counts
  against a separate limit of 20 an hour per user. Wait and retry.
- **The import stops on the GitHub App installation.** The clone refuses an
  installation your GitHub account does not hold. Select an installation you
  own, or install the App on that account first.

## Next

- [Projects](/guides/projects) for what the project owns
- [Builds](/builds/overview) for what a build records
- [Workspaces](/guides/workspaces) for who can see the project
