Project Structure

Every file and directory the Manic framework expects, with conventions and escape hatches.

Project Structure

Manic is built around a small set of conventions. As long as your files live in the right places, the framework discovers, bundles, and serves them with no further configuration.

index.tsx
about.tsx
~404.tsx
~500.tsx
~routes.generated.ts
global.css
index.html
main.tsx
manic.config.ts
~manic.ts
package.json
tsconfig.json
.env
.oxlintrc.json
PathRole
app/api/hello/index.tsHono app mounted at /api/hello
app/routes/index.tsx/
app/routes/about.tsx/about
app/routes/posts/index.tsx/posts
app/routes/posts/[id].tsx/posts/:id
app/routes/~404.tsx, ~500.tsxCustom not-found / error UI
app/routes/~components/Colocated UI — ~ excludes from URLs
app/~routes.generated.tsAuto-generated manifest — do not edit
public/Static files at site root
manic.config.tsPlugins, providers, router & build
~manic.tsBun server entry
.envLoaded automatically (use .env.local for secrets)
.oxlintrc.jsonOptional oxlint config

Mandatory Files

FilePurposeNotes
~manic.tsBun server entry. Exports { port, fetch }.Cannot be renamed.
app/index.htmlHTML shell that the build engine fills in.Must include a #root element and import main.tsx.
app/main.tsxReact entry — mounts the <Router> on #root.Imported from index.html.
manic.config.tsConfigures plugins, providers, build, and router.Optional but strongly recommended.

~manic.ts

The single Bun-compatible server entry. The build pipeline rewrites the import of ./app/index.html into a static reference, so the same file works in dev and production:

~manic.ts
import { createManicServer } from 'manicjs/server';

const app = await createManicServer();

export default {
  port: Number(Bun.env.PORT) || 6070,
  fetch: app.fetch,
};

createManicServer returns a Hono instance with API routes, static handling, and dev-time middleware already attached.

app/index.html

Plain HTML. Must reference ./main.tsx and contain a mount node — typically <div id="root"></div>. Manic injects per-route prefetch hints, env variables, and View Transition support automatically.

app/main.tsx

app/main.tsx
import { createRoot } from 'react-dom/client';
import { Router } from 'manicjs';

createRoot(document.getElementById('root')!).render(<Router />);

The <Router> component reads the auto-generated route table from window.__MANIC_ROUTES__, which Manic populates during the boot script.

manic.config.ts

manic.config.ts
import { defineConfig } from 'manicjs/config';
import { tailwind } from '@manicjs/tailwind';
import { vercel } from '@manicjs/providers';

export default defineConfig({
  app: { name: 'My App' },
  plugins: [tailwind()],
  providers: [vercel()],
  router: { viewTransitions: true },
});

See the Configuration Reference for the full schema.


Conventions

app/routes/

Every .tsx (or .jsx) file becomes a public route. The default export must be a React component.

FilenameURL
index.tsx/
about.tsx/about
blog/index.tsx/blog
blog/[slug].tsx/blog/:slug
docs/[...path].tsx/docs/:...path

app/api/

Each leaf folder containing an index.ts becomes an API endpoint. The file must default-export a Hono app:

app/api/users/[id]/index.ts
import { Hono } from 'hono';

const app = new Hono();
app.get('/', c => c.json({ id: c.req.param('id') }));

export default app;

Endpoints are mounted at /api/<folder/path>.

The ~ Prefix

Files and folders that begin with ~ are invisible to the router. Use them for:

  • Layout, shared UI (~components/)
  • Hooks and utilities (~hooks/, ~lib/)
  • Tests next to the source (~MyPage.test.tsx)
  • Custom error pages (~404.tsx, ~500.tsx)
  • Anything else you do not want to expose as a URL
✓ app/routes/about.tsx           → /about
✓ app/routes/~Header.tsx         → ignored
✓ app/routes/posts/[id].tsx      → /posts/:id
✓ app/routes/posts/~PostCard.tsx → ignored

Auto-Generated Files

FileGeneratorEditable?
app/~routes.generated.tsDiscovery engine❌ regenerated on every build
bunfig.tomlmanic dev❌ ignored by git, regenerated each run
.manic/manic build❌ build artifact directory

Add the following to .gitignore:

.gitignore
node_modules
.manic/
bunfig.toml
app/~routes.generated.ts
.env.local

Optional Directories

DirectoryPurpose
public/Files served verbatim at the URL root (public/favicon.ico/favicon.ico).
packages/Workspace packages when using a monorepo.
scripts/Project scripts you call manually with bun run.
tests/Unit/integration tests if you prefer not to colocate.

Output Layout (manic build)

After bunx manic build completes, you will find:

server.js

Providers consume this directory and emit platform-specific outputs (.vercel/output, dist/_worker.js, etc.). See the Build Pipeline for the full sequence.


See Also

On this page