For the complete documentation index, see llms.txt. This page is also available as Markdown.

Build Customization

Explains how to modify the web layer that wraps the mini app (HTML, TypeScript, npm dependencies, Vite settings) in a way that survives SDK updates.

What to touch

The build is split into the step where Unity creates the WebGL output and the step where that output is wrapped into a web project and packaged. The internal behavior is Build pipelinein, and here the location the user editsonly.

Step
Output
Edit point

Unity WebGL build

webgl/ (intermediate output)

Do not edit. The settings are Build profile

Granite packaging

ait-build/ait-build/dist/

Assets/WebGLTemplates/AITTemplate/ subfolder — this document

Caution: webgl/and ait-build/files should not be edited directly. webgl/is intermediate output that Unity recreates on every build, and packaging operates based on Assets/WebGLTemplates/AITTemplate/the template, not this folder. Even if you modify both folders, the final package will not reflect those changes and they will disappear on the next build.

Note: The final package used by QR testing and actual deployment is ait-build/dist/. When you open the build result directly, look in this folder.

User area markers

The SDK template is merged with the latest SDK version every time a build starts. At that time only the content between the markers is preservedand anything outside the markers is updated with SDK values. Where and when merging happens for each file is described in Build pipelinethe template merge timing section.

HTML markers

index.htmlprovides two areas.

<!-- USER_HEAD_START - Add your custom scripts/styles in this area -->
<!-- USER_HEAD_END -->

<!-- USER_BODY_END_START - Add your custom scripts in this area -->
<!-- USER_BODY_END_END -->

USER_HEADis inside <head>, and USER_BODY_ENDis just before </body>. is inserted.

TypeScript config file markers

vite.config.ts, granite.config.ts, apps-in-toss.config.tsuse the same pair of markers.

Important: USER_CONFIGIf SDK-managed settings (app name, brand, permissions, webViewProps etc.) are redeclared here, the SDK values win during merge, so it has no effect. The build is fine, but the following warning appears — remove the relevant key from USER_CONFIG.

Conversely SDK_GENERATED If an unreplaced placeholder remains in the area, the build stops with a hard error. In that case, recreate the template with Clean Build.

Customizable files

All files are under Assets/WebGLTemplates/AITTemplate/ .

File
Role
Merge method

index.html

HTML entry point

USER_HEAD / USER_BODY_END Preserve marker area

BuildConfig~/package.json

npm dependencies

Merge dependencies / devDependencies (SDK takes precedence on conflicts)

BuildConfig~/vite.config.ts

Vite build settings

USER_CONFIG Preserve marker area

BuildConfig~/granite.config.ts

Granite packaging settings (2.x)

USER_CONFIG Preserve marker area

BuildConfig~/apps-in-toss.config.ts

Apps in Toss settings (3.x)

USER_CONFIG Preserve marker area. If empty, granite.config.tsof USER_CONFIGautomatically migrates

BuildConfig~/tsconfig.json

TypeScript compiler settings

SDK-required options(moduleResolution, esModuleInterop) are forced to SDK values; otherwise the project values take precedence

BuildConfig~/pnpm-workspace.yaml

pnpm workspace settings

If a project file exists, use it; otherwise copy the SDK file

BuildConfig~/src/

TypeScript entry point and modules

Preserve the entire folder (recursive copy)

BuildConfig~/ Other files

.env, static assets, etc.

Copy all root files and subfolders except those in the exclusion list below

Things excluded from copying other files — root files package.json, pnpm-lock.yaml, pnpm-workspace.yaml, vite.config.ts, tsconfig.json, unity-bridge.ts, granite.config.ts, apps-in-toss.config.ts (each has its own merge path) and folders node_modules/, .npm-cache/, dist/.

Dependency conflict handling: If you add a package already declared by the SDK(@apps-in-toss/web-framework, @apps-in-toss/web-analytics, vite, typescript etc.) at a different version, the SDK version takes precedence. Packages not declared by the SDK (for example: firebase, canvas-confetti) are added as-is.

Note: pnpm-workspace.yamlexists to exemptminimumReleaseAgefrom pnpm's supply-chain protection ( @apps-in-toss/*). Since pnpm reads this setting only from pnpm-workspace.yaml, it must be copied to the build directory. Unless you have a special reason, keep the SDK default.

index.html customization

The file to edit is Assets/WebGLTemplates/AITTemplate/index.html. It must be added _STARTand _END between the markersto be preserved.

USER_HEADis used to declare static resources such as meta tags, fonts, preload hints, and external stylesheets.

USER_BODY_ENDis used to reference the entry point of user code. The recommended pattern is to load the TypeScript entry point as a module — everything imported in the entry point is bundled into a single bundle through Vite's tree shaking and minification.

After the build finishes, ait-build/index.htmlyou can open it and check whether the code you wrote was included. If the following appears in the Unity Console, the merge worked correctly.

TypeScript entry point

User code is written with BuildConfig~/src/main.tsas the entry point. Since Vite bundles this file, npm package imports, tree shaking, and type checking all apply.

BuildConfig~/src/main.ts:

BuildConfig~/tsconfig.jsonIf you put it here, you can customize compiler options. SDK-required options (moduleResolution, esModuleInterop) are forced to SDK values.

Add external libraries

We recommend installing them as npm packages and importing them from the entry point. Versions are pinned, ensuring build reproducibility; you are not affected by CDN outages or network blocking; and tree shaking and minification are applied.

The procedure is the same regardless of the library — package.jsonadd dependency to main.ts→ import from index.html→ reference the entry point in. See the tutorial below for a concrete example.

Alternative: load directly from a CDN

If you just want to try it quickly without build tools, USER_HEADto <script src="...">you can load it directly with . However, if the CDN fails the app cannot load, versions are baked into the URL so reproducibility is worse, and you do not get tree shaking or type checking. It is not recommended for everyday use.

Customize Vite settings

BuildConfig~/vite.config.tsof USER_CONFIG Add plugins or build options in the section.

granite.config.tsand apps-in-toss.config.tsalso provides the same USER_CONFIG section.

Using React components

To implement a UI overlay with React, add React dependencies and a Vite plugin to the external library addition and TypeScript entry point flow.

BuildConfig~/package.json:

BuildConfig~/tsconfig.json:

BuildConfig~/vite.config.ts:

BuildConfig~/src/main.tsx:

index.html:

Build output structure

After packaging finishes, ait-build/the following structure is created in

node_modulesand pnpm-lock.yamlis preserved even during rebuilds, improving build speed.

Behavior on SDK update

Even if you update the SDK, your customizations are automatically preserved.

Situation
Behavior

Template with markers

Preserve user area, update only SDK area

Older template without markers

Replace the entire file with the new SDK template + manual migration warning

Existing without markers index.htmlis fully replaced and the following warning is shown. Move the custom parts of the backed-up old file into the marker area of the new template.

When merged successfully, logs like the following are left.

tutorial

The two tutorials below (#1 canvas-confetti, #2 Firebase Analytics) are actually built by E2E tests and verified in a browser. The code blocks are exactly in the form the tests expect, so it is safer to follow them as-is first and change them afterward.

Add screen effects with canvas-confetti

canvas-confettiThe simplest example of bundling it to show a confetti effect on page load. You can learn the whole external library addition flow at once.

1. BuildConfig~/package.jsonAdd dependency to

2. BuildConfig~/src/main.ts Write

3. index.htmlreference the entry point in

4. Check after build

If you run the build and open the output in the browser, confetti bursts onto the screen immediately after the page loads. If you see confetti is not definedin the console, recheck the entry point reference or package.json dependency addition step.

Firebase Analytics integration

Bundle the Firebase Web SDK (Modular SDK) to connect app initialization and Analytics. Inject the API key via .env— this keeps the key out of code commits and lets you use different values per environment.

1. BuildConfig~/package.jsonAdd dependency to

2. Assets/WebGLTemplates/AITTemplate/BuildConfig~/.env Write

This file is automatically copied to ait-build/.envduring the build and used by Vite.

Vite VITE_ only exposes environment variables with this prefix to the client bundle. If you use a different prefix, import.meta.envcannot read them.

.gitignore Configuration: .envcontains secret keys, so add both of the following paths to ignore. A common place for shared team defaults is .env.example.

3. BuildConfig~/src/main.ts Write

4. index.htmlreference the entry point in

5. Check after build

In the browser developer tools console, you can check the following.

You can also verify real-time event reception in Firebase Console's Analytics > DebugView (debug mode must be enabled — official documentation See).

To apply the two tutorials together: package.jsonadd both dependencies to main.tsplace the two import blocks in order in. One entry point (src/main.ts) is sufficient.

Was this helpful?