Apps as Templates

Use renoun apps as living templates that you extend through overrides—a modern alternative to traditional scaffolding.

A renoun app is a complete, runnable application that lives in node_modules and serves as a template you can extend. Think of apps like @renoun/blog, @renoun/docs, or a Presenter-style app as bases that you customize by adding your own files.

This “Apps as Templates” approach differs from traditional scaffolding:

Traditional TemplatesApps as Templates
Scaffold once → own all the codeDepend on the app → override what you need
Hard to updateUpgradeable via npm
You maintain everythingYou only own your customizations

When you run renoun dev or renoun build:

  • The renoun app is the base .
  • Your files override the app’s files additively.
  • renoun builds a runtime under .renoun/ , merges both, and launches the framework there.

If you already have your own Next/Vite/Waku app and just want renoun inside it, use
”Use renoun in your app” (renoun next dev, renoun vite dev, etc.) instead.


How it works

The “Apps as Templates” system works like Docker’s image layers—you have a base image (the app) and your overrides on top. When you run renoun dev, the CLI merges them:

Your Project (your overrides)
├── package.json
├── posts/                      ← your content
│   └── hello-world.mdx
└── .renoun/app/-renoun-blog/   ← merged runtime
    ├── ...                     ← base (from the app)
    ├── posts/                  ← your overrides (linked to your posts/)
    └── node_modules/           ← shared dependencies
  1. Runtime preparation The CLI creates .renoun/app/<app-name> and copies the base app’s files into it. Only node_modules is symlinked to avoid duplication.
  2. Override application Your files are applied on top using an additive model—files you create are linked into the runtime, merging with the base app rather than replacing it entirely.
  3. Framework launch The CLI spawns the framework ( next dev , vite dev , waku dev , etc.) inside the runtime directory . The framework sees the merged result: base app + your customizations.
  4. Path resolution renoun sets an internal environment variable so its utilities resolve paths relative to the runtime directory, ensuring your overridden content is discovered correctly.

This architecture means you never modify the template in node_modules; you only add and edit files in your own project. When the base app updates, you get the improvements automatically.


Quick start

  1. Install the renoun runtime and a template app:
    pnpm add renoun @renoun/blog
    
  2. Add scripts that call into the renoun CLI:
    {
      "scripts": {
        "dev": "renoun dev",
        "build": "renoun build"
      }
    }
    
  3. Create your content—files that override parts of the template:
    posts/hello-world.mdx
    
  4. Run your customized app:
    pnpm run dev
    
    See examples/blog for a complete example that extends the @renoun/blog template with custom posts.

The CLI merges the template into .renoun/app/<package-name>, applies your overrides on top, and launches the framework (next dev, vite dev, or waku dev). You see the combined result—your customizations on top of the base app.

Overriding files

Every file or directory that you place next to your package.json—excluding lockfiles and node_modules—becomes an override. During startup renoun:

  1. Copies template files into the runtime directory. All files are copied so bundlers resolve imports correctly. Only node_modules is symlinked to avoid duplication.
  2. Applies your overrides using an additive model that intelligently merges your files with the template’s files. Your content is added to the base, not replacing everything.
  3. Enables hot reload because the framework watches the linked files, so edits to your source files trigger rebuilds immediately.

Additive overrides

The override system uses an additive model rather than a replacement model. This means:

  • Your files are merged with the template’s files, not replacing them entirely
  • You can override specific files while keeping the rest of the app intact
  • File metadata is preserved (modification times, etc.) since hard links reference the original files
  • No disk space duplication since hard links point to the same data on disk

For example, if you create posts/my-post.mdx and the template already has posts/default.mdx:

Your Project                        Runtime Directory
├── posts/                          ├── posts/
│   └── my-post.mdx                 │   ├── default.mdx     ← from template
└── ...                             │   └── my-post.mdx     ← hard link to yours
                                    └── ...

Both files are visible in the runtime—your content is added alongside the template’s defaults. This additive approach lets you extend templates without losing their default content.

Overriding files

To replace a component shipped with the template, add a file with the same relative path:

// ui/RootProvider.tsx
import {
  RootProvider as BaseRootProvider,
  type RootProviderProps,
} from 'renoun'

export function RootProvider(props: RootProviderProps) {
  return <BaseRootProvider {...props} theme="nord" />
}

The runtime link points to your implementation, so the framework reloads with your changes without extra configuration. Your file takes precedence over the template’s version at that path.


Ejecting from a template

Using a template from node_modules is great for exploring and making light customizations. When you’re ready to fully own the source, you can eject.

Ejecting turns:

“Extend a template from node_modules” into “Use renoun in your app.”

What renoun eject does

When you run:

renoun eject

renoun will:

  1. Materialize the runtime into your repo
    • Take the current runtime at .renoun/app/<app-name> .
    • Copy all files (e.g. app/ , components/ , ui/ , configs) into your project root (or a configured target directory).
    • Preserve paths so the layout matches what you were already running.
  2. Apply your overrides on top
    • Any files or directories you were overriding (like posts/ or ui/RootProvider.tsx ) are treated as the source of truth.
    • If a file exists both in the template and your project, the local file wins .
    • Overridden files fully replace the template’s version.
    The result is a single, coherent codebase that reflects exactly what you were seeing while running the app.
  3. Remove the template dependency
    • The ejected app no longer depends on the original template package at runtime.
    • renoun eject removes the specified app from your dependencies / devDependencies , leaving renoun itself in place.
  4. Clean up the runtime directory
    • The .renoun/ directory is automatically deleted since you no longer need the runtime environment.
  5. Prepare you to run it as your own app After ejecting, your app lives directly in your repo. You can switch to running it like any other framework app with renoun:
    {
      "scripts": {
        "dev": "renoun next dev",
        "build": "renoun next build"
      }
    }
    
    (Swap next for vite / waku depending on the framework the app uses.)

Now you’re using the “Use renoun in your app” flow, with the ejected codebase as your app.

When to eject

Stay in the “Apps as Templates” flow (renoun dev) when:

  • You want to try a blog/docs/Presenter app quickly.
  • You’re happy with the overall structure and only need targeted customizations.
  • You want to keep upgrading the underlying template over time.

Use renoun eject when:

  • You’re turning the app into a long-lived product with heavy customizations.
  • You want full control over routing, layout, configuration, and dependencies.
  • You’re ready to treat it as your own app and run it via renoun next dev / renoun vite dev / renoun waku dev .

Building your own app template

Creating an app template is straightforward—it’s just a normal application with a few conventions. Here’s how to build one that works well with renoun’s override system.

Basic structure

An app template is a publishable npm package containing a complete, runnable application:

blog-template/
├── app/                    # Your framework routes
│   ├── layout.tsx
│   └── page.tsx
├── posts/                  # Content directory for user content
│   └── .gitkeep            # Preserves empty directory in git
├── collections.ts          # renoun collections
├── package.json            # Package with framework deps
└── README.md

Package setup

Your package.json should include the framework and renoun as dependencies:

{
  "name": "@your-org/my-template",
  "version": "1.0.0",
  "dependencies": {
    "renoun": "^7.0.0",
    "next": "^15.0.0",
    "react": "^19.0.0"
  }
}

Users install your template alongside renoun, then run renoun dev to use it as their base.

Design for overrides

When building a template, think about what users will want to customize:

  • Content directories (like posts/ , docs/ ) — Users add their content here
  • UI components — Provide sensible defaults that users can override
  • Configuration — Keep config in discoverable locations

User files are merged additively with your template, so your defaults show up alongside user content. Design your template to work well with this model.

Gotchas

A few things to watch out for when building templates:

Empty directories aren’t tracked by git. If your template includes empty directories that users are expected to add content to (like posts/), add a .gitkeep file:

touch posts/.gitkeep

Without this, the directory won’t exist when users install your template from npm. Hidden files like .gitkeep are automatically excluded from directory listings, so they won’t appear as content entries.

Build artifacts should be ignored. Add .renoun/, .next/, out/, and node_modules/ to your .gitignore. The eject command automatically skips these directories.

Test the eject flow. Run renoun eject on your own template to verify the experience is smooth. Users who outgrow the template model will appreciate a clean transition.

Example

See the @renoun/blog source for a complete example of a well-structured app template.


Cleaning up

Stop the process with Ctrl+C just like any framework script. The next run recreates the runtime folder from scratch, ensuring you always start from a clean merge of the template when using renoun dev.

After ejecting, the .renoun/ directory is automatically removed since the ejected app no longer needs the runtime environment.


Security model

Running templates this way is designed with security in mind. Here’s how the system protects your project:

What renoun controls

The CLI uses an internal environment variable (RENOUN_RUNTIME_DIRECTORY) to tell renoun utilities where the runtime directory is. This allows file discovery to work correctly in the merged environment. Before trusting this variable, renoun validates it with multiple checks:

CheckWhat it prevents
Real path resolutionFollows symlinks and resolves ../ before validation, preventing path traversal attacks
Containment verificationThe resolved path must contain /.renoun/, ensuring it’s a renoun-managed directory
Workspace validationThe parent directory must contain package.json or pnpm-workspace.yaml, confirming it’s a real project

If any check fails, renoun ignores the environment variable and falls back to normal path resolution. This means even if an attacker sets the variable, they can’t redirect renoun to arbitrary directories.

What you control

While renoun secures its internal operations, you should understand these aspects of the system:

  • Template apps run code in your environment. Only install templates from sources you trust, just as you would with any npm dependency. Template apps have the same access as any other package in your project.
  • The .renoun/ directory is temporary. Add it to your .gitignore . It’s recreated fresh on each run, so don’t store anything important there.
  • Links point to your real files. When you override a file, the framework reads directly from your source. This is the intended behavior—it enables hot reload and live editing.

Shared environments

If you’re running renoun in a shared server or CI environment where other processes might set environment variables before the CLI starts, be aware that environment variable injection is a general security concern for any application. renoun’s validation mitigates this for its specific use case, but standard environment isolation practices still apply.

Summary

Using “Apps as Templates” via renoun dev is safe for normal development workflows. The security model ensures renoun only operates within your project’s .renoun/ directory, and the same trust model applies to template apps as to any npm package you install. If you eject, you keep all the code locally and continue building as your own app with the same safety guarantees.