All project settings live in morph.config.json at the project root.
{
"name": "my-app",
"entry": "src/App.mx",
"output": ".morph/output",
"window": {
"width": 800,
"height": 600,
"title": "Morph App"
},
"renderer": "flash",
"types": "infer",
"runtime": {
"type": "cpp",
"version": "0.1.0"
},
"dependencies": {},
"cpp_sources": [],
"native": {
"include_dirs": [],
"library_dirs": [],
"libraries": [],
"cflags": [],
"ldflags": []
},
"build": {
"wayland": false,
"system_freetype": false,
"upx": true,
"upx_version": "",
"cxx": "",
"dev_cxx": "",
"cmake": ""
},
"lint": {
"disable": [],
"severities": {}
}
}| Field | Type | Default | Description |
|---|---|---|---|
name |
string | "my-app" |
Project name. Used in window title and build output. |
entry |
string | "src/App.mx" |
Root .mx / .tsx / .ts file to compile. |
output |
string | ".morph/output" |
Directory for build artifacts. |
window |
object | (see below) | Native window settings. |
renderer |
string | "flash" |
Renderer backend: "flash" (default, lightweight) or "forge" (retained surfaces, beta). |
types |
string | "infer" |
Type mode for translated logic, same as morph --types: "infer" (default, analyze code) or "strict" (respect annotations). CLI --types overrides it. |
runtime |
object | (see below) | Runtime type and version to download/use. |
dependencies |
object | {} |
Package dependencies (reserved for future package manager). |
cpp_sources |
array | [] |
C++ source files to include in the build. |
native |
object | (see below) | Build options for imported C++ files. |
build |
object | (see below) | Static linking and compression settings. |
lint |
object | (see below) | Lint rule overrides for .mx / .tsx / .ts files. |
window| Field | Type | Default | Description |
|---|---|---|---|
width |
int | 800 |
Window width in CSS pixels. |
height |
int | 600 |
Window height in CSS pixels. |
title |
string | "Morph App" |
Window title bar text. |
Window size limits (minWidth, minHeight, maxWidth, maxHeight) are not read from morph.config.json — set them via the windowConfig export or <morph-window> props instead (below).
Window settings can also be overridden per-file by exporting windowConfig from your entry file:
export const windowConfig = {
title: "Calculator",
width: 340,
height: 500,
minWidth: 340,
minHeight: 500,
maxWidth: 340,
maxHeight: 500,
}runtimeControls which runtime version is downloaded and linked.
| Field | Type | Default | Description |
|---|---|---|---|
type |
string | "cpp" |
Runtime flavor: "cpp" (current) or "rust" (future). |
version |
string | "0.1.0" |
Semantic version. Must match a release on GitHub. |
{
"runtime": {
"type": "cpp",
"version": "0.2.0"
}
}Run morph update --runtime to upgrade. The lock file (morph.lock) records the exact version + SHA256 hash for reproducibility.
nativeOptions forwarded to g++/clang++ when importing user C++ files (import { fn } from './file.cpp').
| Field | Type | Default | Description |
|---|---|---|---|
include_dirs |
string[] | [] |
Additional -I paths for headers. |
library_dirs |
string[] | [] |
Additional -L paths for archives. |
libraries |
string[] | [] |
Libraries to link (-l flags without the -l prefix). |
cflags |
string[] | [] |
Extra compile flags (e.g. ["-O3"]). |
ldflags |
string[] | [] |
Extra link flags. |
Example:
{
"native": {
"include_dirs": ["libs/include"],
"libraries": ["png", "z"],
"cflags": ["-O3", "-march=native"]
}
}build| Field | Type | Default | Description |
|---|---|---|---|
wayland |
bool | false |
Enable GLFW Wayland backend (adds ~150 KB + deps). |
system_freetype |
bool | false |
Use system libfreetype.a instead of the trimmed self-built copy. |
upx |
bool | true |
Compress the final binary with UPX. |
upx_version |
string | "" |
Pin a specific UPX release (e.g. "4.2.4"). Empty uses system/default. |
cxx |
string | "" |
Compiler for production binaries (morph build). Empty = auto (g++ on Linux, clang++ on macOS). |
dev_cxx |
string | "" |
Compiler for dev-mode hot-reload logic. Empty = auto (newest clang++, else g++). |
cmake |
string | "" |
CMake binary used to build the dev runtime. Empty = "cmake" on PATH. |
Morph uses two independently configurable toolchains:
morph build, morph run) — uses build.cxx if set, otherwise the platform default. Release binaries have one predictable toolchain.morph dev) — every logic edit recompiles a small .so. The default is g++ on Linux (GCC 14 parses template-heavy headers ~3.5s vs clang ~4.4s for a small app). Opt into clang if your toolchain differs:{
"build": {
"cxx": "/usr/bin/g++-13",
"dev_cxx": "/usr/bin/clang++-19",
"cmake": "/snap/bin/cmake"
}
}The same values can be set per-session with environment variables (config wins over env, env wins over defaults):
export MORPH_CXX=/usr/bin/g++-13
export MORPH_DEV_CXX=clang++-17
export MORPH_CMAKE=/usr/local/bin/cmakeA configured path that doesn't exist falls back to the platform default with a warning rather than failing the build.
Faster dev linker (optional): the hot-reload link step is small (~0.2s), but if you want it anyway, add a faster linker through the existing native flags:
{
"native": {
"ldflags": ["-fuse-ld=mold"]
}
}lint.mx / .tsx / .ts files are checked against the framework's supported surface (elements, props, CSS properties, JS API). Errors block morph build and dev-mode reloads (Next.js-style — dev keeps watching and hot-reloads once fixed); warnings are reported only. Run morph check for a lint-only pass.
| Field | Type | Default | Description |
|---|---|---|---|
disable |
string[] | [] |
Rule codes to turn off entirely, e.g. ["mx-list-key"]. |
severities |
object | {} |
Rule code → "error" / "warning" overrides, e.g. {"mx-tag-stub": "error"}. |
Example:
{
"lint": {
"disable": ["mx-list-key"],
"severities": { "mx-tag-stub": "error" }
}
}| Code | Checks | Default |
|---|---|---|
mx-export |
Exactly one export default function component |
error |
mx-component-name |
Default export is a named function | error |
mx-window-conflict |
<morph-window> and windowConfig used together |
error |
mx-window-missing |
No window is created (no morph-window / windowConfig) |
error |
mx-windowconfig-key / mx-windowconfig-type |
Valid windowConfig keys and types |
error |
mx-window-prop / mx-window-prop-type |
Valid <morph-window> props and types |
error |
mx-tag |
Element is in the supported set | error |
mx-tag-stub |
<input> / <select> / <textarea> not fully implemented |
warning |
mx-prop |
Prop is valid for the element | error |
mx-img-src |
<img> has a src |
error |
mx-event-value |
Event handler is a function | error |
mx-morph-action |
morph-* value is a static string |
error |
mx-key-misuse |
key only inside .map() lists |
warning |
mx-dup-class |
className and class not both used |
warning |
mx-style-prop |
Inline style key is supported | error |
mx-style-value |
Inline style value is valid | warning |
mx-tailwind-class |
className token resolves to a Tailwind class |
warning |
mx-css-prop |
CSS file property is supported | warning |
mx-css-file-missing |
Imported CSS file exists | error |
mx-import-morph |
Imported name is exported by morph |
error |
mx-no-morph-import |
Morph API (morphState, morphEffect, morphShared, morphEvent, useWindow, Window, CSS) is imported from 'morph' before use |
error |
mx-undefined |
Referenced name is declared, imported, or a supported native global | error |
mx-import-type |
Import is .css / .cpp / morph |
warning |
mx-state-scope |
morphState is called inside a component body |
error |
mx-shared-scope |
morphShared is exported at module scope |
error |
mx-event-scope |
morphEvent is exported at module scope |
error |
mx-api-removed |
Removed string-key/shared/event APIs are not used | error |
mx-state-pattern |
morphState destructured as [getter, setter] |
error |
mx-effect-cb |
morphEffect first arg is a function |
error |
mx-effect-deps |
Effect deps are state variables | warning |
mx-transpile |
JS compiles to C++ — JSX expressions, event/effect bodies, component consts, inner functions, global vars, top-level functions | error |
mx-js-global |
Browser/JS globals with no native counterpart (document, window, localStorage, sessionStorage, navigator, location, history, screen, alert, prompt, confirm, requestAnimationFrame) |
error |
mx-js-member |
Member access on unsupported JS builtins (Math.*, JSON.*, Date.*, RegExp.*, console.* other than log/warn/error/info) |
error |
mx-js-method |
Methods the runtime types don't implement (.map()/.split()/.toUpperCase() on state values or string literals, …) |
error |
mx-js-op |
Operators the C++ translator can't emit (typeof, **, instanceof, in, delete, ??=, &&=, ` |
|
mx-js-syntax |
JS constructs the translator can't handle (?., destructuring, generators/yield, for...in/for...of, object spread, spread in call args, object methods/getters) |
error |
mx-list-key |
.map() item root has a key |
warning |
These morph.config.json fields can be overridden via CLI flags on morph build and morph run:
morph build --static # statically link GLFW/FreeType/HarfBuzz
morph build --no-upx # skip UPX compression
morph build --upx-version 4.2 # pin UPX version
morph build --output bin/ # custom output directory
morph build --entry src/App.mx # override entry file