When you run morph new my-app, Morph generates this project layout:
my-app/
├── src/
│ ├── App.mx ← entry point (JSX + CSS + TS)
│ ├── style.css ← stylesheet
│ └── env.d.ts ← TypeScript declarations for editor support
├── morph.config.json ← project configuration
├── morph.lock ← locked runtime version + hash (generated on first build)
├── tsconfig.json ← TypeScript config for editor support
├── .morph/
│ ├── runtime/ ← symlinked to global cache (v0.1.0/)
│ ├── build/ ← build artifacts
│ └── cache/ ← project-specific cache (CSS, fingerprints)
└── dist/
└── app ← compiled binary (generated by morph build/run)src/App.mxThe entry point. An .mx file is a JSX-like format that combines your component logic and markup in a single file. The default export is the root component:
import { morphState } from 'morph'
import "./style.css"
export const windowConfig = { title: "My App", width: 800, height: 600 }
export default function App() {
const [count, setCount] = morphState(0)
return (
<body>
<h1>Hello from Morph</h1>
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
</body>
)
}import "./style.css" — loads a stylesheet (intercepted by the compiler)windowConfig — optional export that overrides morph.config.json window settingsexport default function App() — the root component rendered into the windowsrc/env.d.tsTypeScript declarations that let your editor understand .mx, .css, .cpp, and .h imports. You generally don't need to edit this file.
morph.config.jsonProject configuration — window size, entry point, renderer, build options, runtime version. See Configuration for the full reference.
morph.lockLocks the exact runtime version and content hash. Generated on first morph dev or morph build. Commit this file to ensure reproducible builds across machines.
tsconfig.jsonConfigures your editor's TypeScript language server for .mx files. Points jsxImportSource at the morph module so autocomplete works for morphState, morphEffect, etc. Don't edit unless you know what you're doing.
.morph/ Directory.morph/
├── runtime/ ← symlink to ~/.morph/cache/runtimes/cpp/v0.1.0/
├── build/ ← object files, fingerprints, logic.so (dev mode)
└── cache/
├── css/ ← fetched remote CSS + font files (MD5-cached)
└── *.fingerprint ← content hashes for incremental buildsThis directory is not committed to git (added to .gitignore by morph new).
dist/Build output directory. Contains the compiled binary. Configurable via morph.config.json output field or --output flag.
Not supported yet. Importing other
.mxfiles (import Card from './Card.mx') is on the roadmap — the linter currently warns withmx-import-type. Keep your app in one entry file, or share logic via C++ imports (below).
You can still organize CSS per component:
src/
├── App.mx
├── components/
│ ├── Header.css
│ └── Card.css
└── style.css// src/App.mx
import "./components/Header.css"
import "./components/Card.css"Load stylesheets with a plain import:
import "./style.css"
import "./components/Header.css"Remote stylesheets work too — pass a URL and Morph fetches it, MD5-caches it in .morph/cache/css/, and downloads any @font-face font files it references:
import "https://cdn.example.com/theme.css"Clear the cache with morph cache.
Note: importing other
.mxfiles (import Card from './Card.mx') is not supported yet — keep your app in one entry file, or share logic via C++ imports.
Place .h or .cpp files in the project and reference them in morph.config.json:
{
"cpp_sources": ["cpp/my_widget.h"]
}Or import .cpp files directly in JSX for bidirectional C++/JSX interop — see C++ Interop.