> Markdown version of [Package a Component](https://vaadin.com/docs/next/building-apps/components/package-component). Section index: [llms.txt](https://vaadin.com/docs/next/building-apps/llms.txt)

# Package a Component

A component that lives inside your application is a regular Java class — you create it, style it, and use it alongside all your other code. No special project setup is needed. This article covers the extra steps required when you want to extract a component into its own project and distribute it as a standalone JAR.

Packaging a component as its own project gives you several benefits: a cleaner application project, a separate test and release cycle for the component, better separation of concerns, and easy reuse across multiple projects. Consuming applications add it as a dependency and Vaadin picks up its Java classes and frontend resources automatically. The tradeoff is more project infrastructure — a dedicated POM, a specific directory layout for frontend resources, and a build configuration that keeps Vaadin itself out of the published JAR.

## <a id="when-to-package-a-component"></a>When to Package a Component

Package a component as a separate JAR when:

- **Multiple applications** need the same component.

- **Separate teams** develop the component and the applications that consume it.

- **You plan to publish it** on the [Vaadin Directory](https://vaadin.com/directory) or Maven Central.

If the component is only used in one project, keep it in your application’s source code. Premature extraction adds complexity without benefit.

## <a id="getting-started"></a>Getting Started

Start from the [Vaadin Add-on Starter](https://github.com/vaadin/addon-starter-flow). It provides a ready-made Maven project with the correct directory layout, dependency scopes, Jetty for testing, and Maven profiles for publishing to the Vaadin Directory. The rest of this article explains what the starter gives you and how to customize it. If the component is only for your organization’s own applications, see [Share Components between Applications](https://vaadin.com/docs/next/building-apps/components/share-component.md) for what to change.

The starter project has this structure:

```
my-component/
├── src/
│   ├── main/
│   │   ├── assembly/           (1)
│   │   │   ├── assembly.xml
│   │   │   └── MANIFEST.MF
│   │   ├── frontend/           (2)
│   │   ├── java/
│   │   │   └── org/vaadin/addons/mygroup/
│   │   │       └── TheAddon.java
│   │   └── resources/
│   │       └── META-INF/
│   │           └── frontend/
│   └── test/
│       └── java/
│           └── org/vaadin/addons/mygroup/
│               └── AddonView.java  (3)
└── pom.xml
```

1. Assembly descriptor and manifest for Vaadin Directory publishing (see [Directory Profile](#directory-profile)).

2. Frontend files (TypeScript, CSS) for development. Vaadin picks these up automatically.

3. A demo view in `test` scope — not included in the published JAR.

## <a id="maven-configuration"></a>Maven Configuration

The starter POM is pre-configured. This section explains the key parts you may want to customize.

### <a id="dependencies"></a>Dependencies

The starter declares `vaadin-core` as a compile-time dependency and uses the `maven-jar-plugin` to exclude Vaadin’s build metadata from the published JAR. This prevents version conflicts in consuming applications. If your add-on only needs a subset of Vaadin, you can replace `vaadin-core` with a more specific dependency like `flow-server`:

```xml
<dependencies>
    <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-core</artifactId>
    </dependency>
</dependencies>
```

The Vaadin BOM in `<dependencyManagement>` controls the version. Update `vaadin.version` in `<properties>` to target a different Vaadin release.

### <a id="directory-profile"></a>Directory Profile

The starter includes a `directory` Maven profile that builds a ZIP file for the [Vaadin Directory](https://vaadin.com/directory). It uses the `maven-assembly-plugin` with a descriptor at `src/main/assembly/assembly.xml` and a manifest at `src/main/assembly/MANIFEST.MF`. The profile also attaches source and Javadoc JARs. You don’t need to modify these files unless you want to include extra files in the ZIP.

## <a id="frontend-resources"></a>Frontend Resources

If your add-on includes JavaScript or TypeScript modules, or CSS files that should be included in the frontend bundle, place them under:

```
src/main/resources/META-INF/frontend/
```

Files in this location are automatically discovered by Vaadin when the add-on JAR is on the classpath, and are processed by the Vite build pipeline. Reference them from your Java component with `@JsModule` or `@CssImport`:

```java
@JsModule("./my-component/status-badge.js")
public class StatusBadge extends Composite<HorizontalLayout> {
    // ...
}
```

The `./` prefix maps to the `frontend/` directory on the classpath, which includes your `META-INF/frontend/` files.

During development, you can also use `src/main/frontend/` for convenience — Vaadin picks up files from there as well. However, only files in `META-INF/frontend/` are included in the published JAR.

> **Note: Deprecated Location**
>
> Some earlier add-ons place frontend files in `src/main/resources/META-INF/resources/frontend/` instead. That location still works, but it’s deprecated: everything under `META-INF/resources/` is also published as static resources, so the un-bundled source files are served directly to browsers — which is usually not what you want. Use `META-INF/frontend/` so the files are only used as input for the frontend bundle.

## <a id="including-stylesheets"></a>Including Stylesheets

CSS files typically go in the static resources folder, `src/main/resources/META-INF/resources/`, and are loaded with `@StyleSheet` — the same approach as in application code (see [Style a Component](https://vaadin.com/docs/next/building-apps/components/style-component.md)):

```java
@StyleSheet("my-component/status-badge.css")
public class StatusBadge extends Composite<HorizontalLayout> {
    // ...
}
```

The path resolves against the static resources root, so the example above loads `src/main/resources/META-INF/resources/my-component/status-badge.css`.

If you want the CSS to be included in the frontend bundle instead of served as a separate file, place it in `META-INF/frontend/` and load it with `@CssImport`:

```java
@CssImport("./my-component/status-badge.css")
```

`@CssImport` is also needed to inject styles into a Vaadin component’s shadow DOM, using the `themeFor` attribute:

```java
@CssImport(value = "./my-component/my-grid-styles.css",
           themeFor = "vaadin-grid")
```

See [Styling Components](https://vaadin.com/docs/next/styling/styling-components.md) for details on shadow DOM styling.

## <a id="testing-the-add-on"></a>Testing the Add-On

The starter includes Jetty and is configured with `<defaultGoal>jetty:run</defaultGoal>`, so you can start a local server by running:

```terminal
mvn
```

This starts Jetty with the `test` classpath, which means classes in `src/test/java` are available as routes. The starter includes an `AddonView` demo view — replace it with your own:

```java
@Route("")
public class DemoView extends VerticalLayout {
    public DemoView() {
        StatusBadge success = new StatusBadge("Active", Status.SUCCESS);
        StatusBadge error = new StatusBadge("Failed", Status.ERROR);
        add(success, error);
    }
}
```

Because the demo view is in `src/test/java`, it is not included in the published JAR.

## <a id="building-the-jar"></a>Building the JAR

Build the add-on JAR with:

```terminal
mvn clean install
```

To build the ZIP for the Vaadin Directory:

```terminal
mvn clean install -Pdirectory
```

The resulting JAR (or ZIP) can be used as a dependency in any Vaadin application.

## <a id="pitfalls"></a>Pitfalls

**Don’t bundle Vaadin in the add-on JAR.** The consuming application provides Vaadin at runtime. The starter’s `maven-jar-plugin` configuration excludes Vaadin’s build metadata from the JAR. If you add dependencies, make sure they don’t pull in a second copy of Vaadin.

**Don’t bundle application themes.** Your add-on should not include a full Vaadin theme. It should supply its own styles that work with any theme. Use the theme-neutral `--vaadin-*` custom properties for consistency. See [Use Theme Custom Properties](https://vaadin.com/docs/next/building-apps/components/style-component.md#use-theme-custom-properties) for details.

**Test with the Vaadin versions you support.** If your add-on claims compatibility with a range of Vaadin versions, test it with each. Differences in APIs or behavior between versions can cause subtle bugs.

**Keep the JAR small.** Don’t include test classes, demo applications, or unnecessary resources in the published JAR. The starter keeps demo views in `src/test/java` for this reason.
