The Backstage Roadmap Plugin takes roadmaps out of hidden places like Confluence and puts them front and center. Teams can share what’s coming up, while users get to chime in by suggesting features, voting on ideas, and adding comments. It’s all about creating a space where feedback flows easily, and everyone helps shape the future of the platform together.
🚀 Note: This plugin uses the new Backstage frontend system and the new backend system.
- 📊 Visual roadmap board
- 🗳️ Voting system
- 💬 Comment section for each feature
- 🔐 Role-based permissions (admin vs. regular user)
- 🆕 Feature suggestion form for users
- 🦊 (Optional) GitLab integration — use GitLab issues as the datasource
-
Install the plugin in your Backstage instance:
yarn add @rothenbergt/backstage-plugin-roadmap-backend --cwd packages/backend yarn add @rothenbergt/backstage-plugin-roadmap --cwd packages/app -
Add the plugin to your
packages/backend/src/index.ts:// ... backend.add(import('@rothenbergt/backstage-plugin-roadmap-backend'));
-
The frontend plugin uses the new Backstage frontend system and is automatically discovered. No additional wiring is needed in your app.
After installation, navigate to the /roadmap route in your Backstage instance. From there, you can:
- View the current roadmap
- Vote on features
- Suggest new features
- Comment on existing features
- (Admins) Manage feature statuses
If you aren't utilizing the Backstage permission framework, add the following to your app-config.yaml to enable the Admin Functions for specific users:
roadmap:
adminUsers:
- user:default/admin1
- user:default/admin2Optional roadmap.columns in app-config.yaml customizes each status column on the roadmap board. The backend merges your entries with built-in defaults and exposes the resolved layout to the UI via GET /features/board-config (so the browser does not parse this YAML itself).
Add a list under columns. Each item supports:
| Field | Description |
|---|---|
status |
One of: Suggested, Planned, In Progress (quote in YAML if needed), Completed, Declined. Unknown values are ignored. |
title |
Column heading shown on the board (optional; defaults to the status name). |
visible |
If false, that column is hidden on the board and features in that status are omitted from the default feature list (smaller API payloads). Optional. |
retentionDays |
Positive number: hide features older than this many days in the default list (database datasource only). Optional; omit for no retention. |
retentionAnchor |
created or updated (default updated): which timestamp retention uses. |
Defaults: Every status has a column. In Progress is included but visible: false by default, so the board matches the classic four-column layout until you turn it on. Other statuses default to visible.
Example — each row uses status (required for the merge). The block below shows every optional field at least once: title, visible, retentionDays, and both retentionAnchor values (created / updated). Retention only affects the default feature list when using the database datasource.
roadmap:
columns:
- status: Suggested
title: Ideas
visible: true
retentionDays: 90
retentionAnchor: created
- status: Planned
title: Committed
visible: true
retentionDays: 365
retentionAnchor: updated
- status: 'In Progress'
title: Active work
visible: true
- status: Completed
title: Shipped
visible: true
retentionDays: 30
retentionAnchor: updated
- status: Declined
title: Not doing
visible: falseWith the GitLab datasource, column labels and visibility still apply to how lists are filtered and labeled; retention and “show beyond retention” remain database-only (see the roadmap-backend README for API details).
By default, the plugin stores data in a plugin database. You can optionally use a GitLab project — or an entire GitLab group — as the backend datasource, where roadmap features are stored as GitLab issues.
- Features are created as GitLab issues with a
roadmaplabel - Feature status is tracked via scoped labels (e.g.
roadmap::Suggested,roadmap::In Progress) - Votes and comments are stored as issue notes
- All existing plugin functionality (voting, commenting, status management) works seamlessly through the GitLab API
Set the datasource to gitlab and provide your GitLab connection details in app-config.yaml:
roadmap:
source: gitlab
gitlab:
apiBaseUrl: https://gitlab.com/api/v4
token: ${GITLAB_TOKEN}
projectId: 'your-group/your-project'You can also aggregate roadmap issues across all projects in a GitLab group. Use groupId instead of projectId:
roadmap:
source: gitlab
gitlab:
apiBaseUrl: https://gitlab.com/api/v4
token: ${GITLAB_TOKEN}
groupId: 'my-group'
defaultProjectId: 'my-group/my-project'In group mode, the plugin queries issues at the group level and caches the project each issue belongs to so that subsequent operations (voting, commenting, status updates) target the correct project. The defaultProjectId specifies which project new features are created in.
| Field | Description |
|---|---|
source |
Set to gitlab to enable the GitLab datasource (defaults to database) |
gitlab.apiBaseUrl |
Base URL for the GitLab API |
gitlab.token |
Personal access token with API access to the project or group |
gitlab.projectId |
GitLab project ID (numeric) or URL-encoded path (e.g. your-group/your-project). Mutually exclusive with groupId |
gitlab.groupId |
GitLab group ID or path. Queries roadmap issues across all projects in the group. Mutually exclusive with projectId |
gitlab.defaultProjectId |
Project where new features are created when using group mode. Required for creating features in group mode |
Note: Exactly one of
projectIdorgroupIdmust be provided.
We welcome contributions! Here's how to get started:
- Fork and clone the repository
- Install dependencies:
yarn install(after changing Node major versions, rebuild native modules sobetter-sqlite3matches your runtime; otherwiseplugin.test.tsintegration cases are skipped while the rest of the suite still runs). - Make your changes
- Run tests:
yarn test:all - Run lint:
yarn lint:all
jwa resolutions: jws v3 depends on jwa 1.x and jws v4 on jwa 2.x, so root package.json pins both majors separately. Replacing them with one version would break one of those stacks unless upstream upgrades.
This project uses Changesets to manage versions and releases. When you make a change that affects users, you need to create a changeset:
- Run
yarn changeset - Select which packages are affected (use space to select, enter to confirm)
- Choose the type of change:
- patch - Bug fixes, minor updates
- minor - New features, non-breaking changes
- major - Breaking changes
- Write a short description of your changes
- Commit the changeset file along with your changes
- Create a changeset if needed (see above)
- Push your changes to your fork
- Open a Pull Request with a clear description
- Wait for CI checks to pass
- A maintainer will review your PR
Once merged, your changeset will automatically trigger a "Version Packages" PR, which when merged, publishes the new version to npm!


