Project Summary
Atlassian is proposing a way for Forge developers to use tooling of their choice to build Forge apps by delegating the build process to developer-controlled tooling, thereby removing the requirement to use the built-in, non-configurable Webpack used by the Forge CLI for bundling for backend and frontend functions.
Publish: 09 July 2026
Discuss: 17 July 2026
Resolve: 24 July 2026
Problem
The original vision for the Forge CLI was a “batteries included” experience that favoured consistent, simple deployments over configuration and control. As more complex apps are built and hosted on Forge, this approach is no longer ideal. While the JavaScript ecosystem for packaging and bundling is diverse, Forge is presently restricted to a fixed version of TypeScript and Webpack for both backend and frontend code.
Developers have requested more flexibility in how applications are built, including the use of newer tooling, different frontend bundlers, and custom build processes on the backend. We launched a TypeScript Bundler EAP last year, which attempted to address some of these limitations. However, the feedback we received indicated that the approach was not flexible enough for many use cases.
Solving this problem now will allow developers to leverage modern development ecosystems and custom workflows, leading to improved developer productivity and the ability to build more sophisticated Forge applications, with greater ability for app developers to optimise and control build outputs.
Proposed Solution
Atlassian is exploring a solution where Forge supports delegating the build process to developer-controlled tooling. In this model, the app manifest specifies where the already packaged code resides (for example, a backend/dist directory). The Forge CLI then creates functions and UI Kit modules using that code without further transformation.
Scope
Forge functions and UI Kit modules are supported. There’s no change to Custom UI modules (which are already packaged separately), remotes or containers.
Manifest options
Apps may declare manual packaging for backend functions in the app.runtime by setting package and path. The package: manual@2026 declaration tells the CLI that the functions are packaged manually according to the current version of the standard, and path points to the target directory. Omitting package keeps the current automatic packaging.
app:
runtime:
package: manual@2026
path: backend/dist
name: nodejs24.x
UI Kit resources may declare a prebuilt bundle individually by setting a package property. In this case, path now points to the directory containing the assets instead of a file, as in the following example:
resources:
- key: broccoli
package: manual@2026
path: frontend/broccoli/dist
Manual packaging can be enabled separately for functions and individually for each UI Kit resource (but not per individual backend function).
CLI behaviour
On deploy/build, the Forge CLI will skip compiling and bundling the backend and UI modules that use the manual packaging, and deploy the artefacts prepared by the developer in the specified directories.
It will perform basic linting checks to see whether the contents make sense, but there will be limited linting as we’ll assume the code has already been compiled and bundled.
Tunneling
For backend functions, Forge CLI will watch the contents of the specified directories for changes and automatically reload the functions. It is a responsibility of the developer to recompile the code, or run their packaging tooling in watch mode.
For UI Kit modules, Forge CLI will assume each module will be served by the developer’s local server, similar to the existing implementation for Custom UI modules. It is the responsibility of the developer to start this server and ensure it supports hot module reloading if they want it.
Packaging standard
In order for this to work, pre-packaged code must conform to a defined packaging standard.
Functions
The packaged directory (app.runtime.path) must contain all handlers referenced by function modules in the app manifest.
For every function module with a handler <path>.<export> there must be an exported function <export> in a CommonJS module file <path>.js (or the same path with a different extension loadable by Node.js) inside the output directory.
-
The handler string is
<path>.<export>where the two segments separated by the dot are the relative file path (without extension) and the export name. -
The export must be a function. Both a named export and a declaration like
module.exports.<export>would satisfy this.
For example, the following manifest:
modules:
trigger:
- key: pickle
function: main
events:
- avi:forge:updated:issue
function:
- key: main
handler: cucumber/two.handler
app:
runtime:
package: manual@2026
path: backend/dist
name: nodejs24.x
can be satisfied by having backend/dist/cucumber/two.js export a function named handler.
File and directory names starting with double underscore, such as __durian.js, are reserved and cannot be used by handlers.
Dependencies: All production dependencies (not devDependencies), excluding Atlassian front-end dependencies like @forge/react, that are recursively referenced by the project’s package.json, will be resolved according to Node’s rules for finding node_modules and will be available next to the application code.
This means a developer who wants to compile TypeScript themselves doesn’t need to do anything special to include their dependencies, and can point Forge directly at the compiler’s output directory.
UI Kit
A UI Kit resource with manual packaging points via path at an output directory containing the bundle directory. The directory must contain empty HTML files (see name requirements below) and JavaScript files with the UI Kit code and assets required by them.
For a single-entry resource, the HTML must be named index.html. For a multi-entry resource, provide one HTML file per entry, named as declared in the entry map. For example:
resources:
- key: eggplant
package: manual@2026
path: frontend/eggplant/build # must contain global.html and settings.html
entry:
global: global.html
settings: settings.html
- key: feijoa
package: manual@2026
path: frontend/feijoa/build # must contain index.html
The HTML files should be empty, but they must reference all JavaScript that is needed for a particular resource in the body, using relative paths. For example, frontend/feijoa/build/index.html could look like:
<!doctype html>
<html>
<head></head>
<body>
<script type="module" src="./index.js"></script>
</body>
</html>
The JavaScript files must:
-
Not contain any JSX syntax - it must be transformed already using
React.createElement pragma(e.g. Vite@vitejs/plugin-react, Rollup@rollup/plugin-babel, esbuild--jsx-factory=React.createElement). -
Define the application component according to UI Kit documentation in exactly one JavaScript file per resource.
-
Use asset paths relative to the resource root. This is non-default for some tools — e.g. Webpack and Vite default to an absolute
/base and needbase: './'.
File and directory names starting with double underscore, such as __garlic.js, are reserved and cannot appear in the output directory.
Compatibility
- The packaging standard itself would be versioned (eg.
manual@2026) and over time may require changes. These will comply with the Forge deprecation policy, and we would strive to minimise un-necessary changes. - The existing built-in Forge bundler would not be removed and remain available for app developers who have no preference on bundling/packaging tooling. On-going enhancements to the built-in bundler (such as upgrading the Typescript version) would not be paused or discarded as a result of adding the custom packaging capability.
- While the TypeScript Bundler EAP is closed, the underlying implementation will be removed once the manual packaging feature is implemented.
Asks
While we would appreciate any reactions you have to this RFC (even sharing that this would be valuable for you is helpful), we’re especially interested in learning more about:
-
If you have or are considering a custom packaging solution for your Forge app, does the proposed solution make it easier for you? What custom packaging solution do you want to use?
-
What are your concerns with the proposed tunnel workflows, specifically regarding the responsibility of recompiling code or running packaging tooling in watch mode?
-
Are there specific limitations in the proposed packaging standard (such as the CommonJS requirement for functions or the HTML structure for UI Kit) that would get in your way or prevent you from using it?
-
Are there other ways this change may impact your development workflow that we haven’t anticipated?