Configuration Reference

Tamer4Lynx uses three configuration files. Each serves a different purpose.


1. Project config: tamer.config.json

Location: Project root (where you run t4l).

Purpose: Defines the host native app: Android/iOS project paths, app identity, Lynx bundle location, dev server, and autolinking. Used by the CLI for t4l create android, t4l create ios, t4l link, t4l build, t4l start, etc.

Create it with t4l init or t4l.

Signing and production builds (-p)

Store-ready / signed native builds use t4l build [platform] -p (or --production). That is separate from -r (unsigned release without the dev client).

  1. Run t4l signing, t4l signing android, or t4l signing ios to write signing-related fields into tamer.config.json (and optionally append keystore password env var names to .env / .env.local on Android). See the full behavior in Commands under t4l signing and t4l build.
  2. t4l build android -p or t4l build ios -p — each command targets one platform and requires signing for that platform only.

On iOS, -p builds for iphoneos (not the simulator). For App Store submission you still typically archive and export an IPA in Xcode; the CLI documents that limitation in Commands.

Fields

FieldTypeDescription
androidobjectAndroid app configuration
android.appNamestringDisplay name of the Android app
android.packageNamestringApplication ID (e.g. com.example.myapp)
android.sdkstringPath to Android SDK (supports ~, $ANDROID_HOME)
android.deepLinksarrayDeep link configs for intent filters. Each: { scheme, host?, pathPrefix?, activity? }
android.abiFiltersstring[]NDK ABI filters (default: armeabi-v7a, arm64-v8a)
iosobjectiOS app configuration
ios.appNamestringDisplay name of the iOS app
ios.bundleIdstringBundle identifier (e.g. com.example.MyApp)
ios.urlSchemesarrayURL schemes for deep linking. Each: { scheme, role? }
ios.architecturesstring[]Build architectures (default: arm64)
lynxProjectstringPath to Lynx project (e.g. packages/example). Omit for single-folder layout (Lynx app is project root) or to auto-discover via workspaces
pathsobjectOverride default paths
paths.androidDirstringAndroid project directory (default: android)
paths.iosDirstringiOS project directory (default: ios)
paths.lynxProjectstringSame as top-level lynxProject
paths.lynxBundleRootstringBundle output dir inside Lynx project (default: dist)
paths.lynxBundleFilestringPrimary bundle filename (default: main.lynx.bundle); used as default URL in dev meta.json
paths.lynxAdditionalBundlesstring[]More bundle filenames in the same lynxBundleRoot (e.g. auth.lynx.bundle). t4l start lists them in meta.json (bundles), and t4l bundle / embeddable checks that each file exists after npm run build. Assets are still copied from the whole output dir.
devobjectDev mode configuration (optional). Dev client inclusion is now controlled by build type: debug (-d) embeds the dev client when @tamer4lynx/tamer-dev-client is installed; release (-r) omits it.
dev.modestringstandalone | embedded | off. Prefer -d / -r for dev client behavior when applicable.
devServerobjectDev server for HMR (host, port). Used when dev client is present.
devServer.hoststringHost for native app to connect (e.g. 10.0.2.2 for Android emulator)
devServer.portnumberDev server port (default: 3000)
devServer.httpPortnumberAlternative to port
iconstring | objectApp icon: path string, or { source?, android?, ios?, androidAdaptive? }. androidAdaptive (Android 8+): { foreground, background? } or { foreground, backgroundColor? }. Optional layout: foregroundScale: 0–1 uniform shrink (e.g. 0.62); foregroundPadding: extra inset as number (% per side), "8dp", "10%", or { left, top, right, bottom }. Scale and padding are merged into <layer-list> insets. t4l build / t4l bundle refreshes icons.
autolinkbooleanWhen true, t4l link runs after npm install (if postinstall is configured)
syncTamerComponentTypesbooleanWhen not false (default), t4l init / t4l link generate .tamer/tamer-components.d.ts and update tsconfig include. Set to false to skip

Besides Podfile and LynxInitProcessor.swift, t4l link ios (and any command that runs iOS autolink) may generate:

FileWhenPurpose
tamer-host-native-modules.json@tamer4lynx/tamer-dev-client is in node_modulesLists moduleClassName values for dev-client compatibility with dev server meta.json; copied into the app bundle as a resource

Commit this JSON (or re-run t4l link ios on CI) so the app stays aligned with linked native packages.

Examples

Single-folder layout (Lynx app, config, android/, ios/ in one directory — omit lynxProject):

{
  "android": { "appName": "MyApp", "packageName": "com.example.myapp", "sdk": "~/Library/Android/sdk" },
  "ios": { "appName": "MyApp", "bundleId": "com.example.MyApp" },
  "paths": { "androidDir": "android", "iosDir": "ios" },
  "autolink": true
}

Monorepo layout (Lynx app in a package):

{
  "android": { "appName": "MyApp", "packageName": "com.example.myapp", "sdk": "~/Library/Android/sdk" },
  "ios": { "appName": "MyApp", "bundleId": "com.example.MyApp" },
  "lynxProject": "packages/example",
  "paths": { "androidDir": "android", "iosDir": "ios" },
  "autolink": true
}

Android adaptive icon (light logo on dark): set foreground to the logo asset and backgroundColor instead of a background image:

{
  "icon": {
    "source": "packages/example/src/assets/logo.png",
    "androidAdaptive": {
      "foreground": "packages/example/src/assets/logo.png",
      "backgroundColor": "#000000"
    }
  }
}

If the logo is clipped by the launcher mask, shrink and/or pad the foreground layer:

{
  "androidAdaptive": {
    "foreground": "packages/example/src/assets/logo.png",
    "backgroundColor": "#000000",
    "foregroundScale": 0.62,
    "foregroundPadding": 6
  }
}

foregroundScale=0.62 adds ((1−0.62)/2)*100 ≈ 19% inset on each side. foregroundPadding=6 adds another 6% per side. Both are merged into android:left/top/right/bottom on a <layer-list> item — no ScaleDrawable quirks.

Keep source (and optional ios) so fallback launcher mipmaps and the iOS app icon still resolve from the same file.


2. Component config: tamer.config.ts (or .mjs / .js)

Location: Project root or in packages that export ./tamer.config.

Purpose: Rsbuild plugin configuration. Used by tamer-plugin to load plugin instances (e.g. tamer-router, file-based routing). This is not the same as tamer.config.json — it is a TypeScript/JS module that exports plugin defaults.

Used by: pluginTamer() in your lynx.config.ts. Do not import tamer.config in lynx.config; use pluginTamer() so it loads tamer.config internally.

Format

Export an object (default or tamerDefaults) with keys such as:

  • rsbuildConfig — partial Rsbuild config (e.g. source.preEntry)
  • Plugin keys — e.g. tamerRouter for tamer-router's plugin

Example

import { tamerRouterPlugin } from '@tamer4lynx/tamer-router'

export default {
  tamerRouter: tamerRouterPlugin({
    root: './src/pages',
    output: './src/generated/_generated_routes.tsx',
    layoutFilename: '_layout.tsx',
  }),
}

Discovery order

  1. Project root: tamer.config.tstamer.config.mjstamer.config.js
  2. Workspace packages that export ./tamer.config
  3. node_modules packages that export ./tamer.config

See tamer-plugin for details.


3. Extension config: lynx.ext.json

Location: Inside each native extension package (e.g. node_modules/@tamer4lynx/jiggle/lynx.ext.json).

Purpose: Declares how the extension integrates with Android and iOS. Used by the autolinker (t4l link) to add the native module to Gradle and Podfile, register modules/elements, and sync permissions.

Tamer4Lynx follows the Lynx Autolink RFC. If you want to help improve the RFC or lynx.ext.json format, see the RFC discussion.

Format (RFC platforms)

{
  "platforms": {
    "android": { ... },
    "ios": { ... },
    "web": {}
  }
}

Android fields

FieldTypeDescription
packageNamestringJava/Kotlin package (e.g. com.example.mymodule)
moduleClassNamestringNative module class (e.g. com.example.mymodule.MyModule)
moduleClassNamesstring[]Multiple modules
sourceDirstringPath to Android source (default: android)
elementsobjectCustom elements: { "tag-name": "ClassName" }
permissionsstring[]Android permissions to merge into manifest
attachHostViewbooleanAttach module to host view lifecycle
activityPatchesobjectActivity lifecycle hooks: onCreate, onResume, etc.

iOS fields

FieldTypeDescription
podspecPathstringPath to podspec directory (e.g. ios/mymodule)
moduleClassNamestringNative module class name
moduleClassNamesstring[]Multiple modules
elementsobjectCustom elements: { "tag-name": "ClassName" }
iosPermissionsobjectInfo.plist keys: { "NSCameraUsageDescription": "..." }
attachHostViewbooleanAttach module to host view lifecycle

Example

{
  "platforms": {
    "android": {
      "packageName": "com.nanofuxion.vibration",
      "moduleClassName": "com.nanofuxion.vibration.JiggleModule",
      "sourceDir": "android"
    },
    "ios": {
      "podspecPath": "ios/jiggle",
      "moduleClassName": "JiggleModule"
    },
    "web": {}
  }
}

tamer.json (flat format)

Tamer4Lynx also supports tamer.json (flat format). Prefer lynx.ext.json with platforms for new extensions.


Summary

ConfigLocationUsed byPurpose
tamer.config.jsonProject roott4l CLIHost app: Android/iOS identity, paths, dev server, autolink
tamer.config.tsProject root, packagestamer-pluginRsbuild plugins (routing, etc.)
lynx.ext.jsonExtension packaget4l link (autolinker)Native module registration per platform