Mininim

Mininim is a general purpose application framework and library for Nim. Development started in early 2025 while I was still working at Jobs for the Future (JFF) and came out of personal research into high-performance options for REST APIs and microservices. Nim was chosen primarily for its comprehensive meta-programming features which enable the development of small DSLs and custom patterns that further enable extremely rapid development and prototyping.

Investigation and development was re-ignited in the fall of 2025 to help fill future needs related to Primd with the project being officially transferred in December. This site, uses Mininim's dynamic templating and "pages" middleware (modeled after my PHP Framework, Hiraeth) to provide a simple portfolio solution while forwarding development via a real-world use case. Though specific features will be prioritized around Primd's use cases, the general more goals remain the same:

  • Component-based server-side dynanmic templating for integration with HTMX and AlpineJS.
  • Easy addition of new routes and commands via local behavior.
  • Extensibile and modular design for a la carte feature requirements.

Managing multiple early development projects with common dependencies in Nim lead to frustration with their existing package management solutions. After some investigation, I decided to write my own, primarily to avoid what I considered fundamental design flaws in existing solutions. Although built in and for Nim, initially, Percy may become a more generic package manager.

Below you can find an overview of some of the features and a preview into how it looks to develop in Mininim's web modules.

Pages Middleware

    
        import
    dotenv,
    mininim/loader,
    mininim/dic,
    mininim/cli,
    mininim/mdlw/pages

if os.fileExists(".env"):
    dotenv.load()

loader.scan("./local")

var
    app = App.init()
    console = app.get(Console)

quit(console.run())
    

The "pages" middleware is designed to allow direct to template routing based simple directory structure. You can think of it as something of a return to static HTML files with the primary caveat being that you have the full power of the templating language. Addint the functionality to a standard Mininim application is as simple import mininim/mdlw/pages with the mininim/web package installed.

Within the pages module, the middleware is automatically registered via Mininim's unique shape directive. By importing the middleware, the application will also import the requisite CLI command to start the HTTP Server.

    
        shape Pages: @[
    Middleware(
        name: "pages",
        priority: 900
    )
]
    

Adding Pages and Components

Adding a page is as simple as adding a file starting with an @ symbol to the resources/pages directory. For example, we could add the following to resources/pages/@index.html to create our initial landing page.

    
        <x:main>
    <h1>Hello World!</h1>
</x:main>
    

This particular example, however, won't work until we've defined our <x:main> tag in resources/tags/main.html. In this example, our custom component includes the overall site structure. Note the use of the <raw> tag, which enables the content of our {{ children }} to be parsed and rendered as if it were part of the original DOM. As you've probably inferred, an <x:*> tag maps to files located in the resources/tags directory. All subsequent : are converted to / allowing for comprehensive nesting/namespacing support.

    
        <html>
    <head>
        <meta charset="UTF-8" />
        <title>
            {{ title ? "Matthew Sahagian : Portfolio" }}
        </title>
    </head>
    <body class="max-w-6xl mx-auto">
        <raw>
            {{ children }}
        </raw>
    </body>
</html>
    

Component Scope

Components have an isolated scope. This means that whenever you call a component from another template, the only variables available within that component are those that are passed by attributes to the component. However, any children passed into the component are rendered in the calling scope. For example, the name variable is not directly available in the component itself but the children being passed in can use it. Conversely, the title variable will be available in the component, but not to the children.

    
        <set name="Bob" />
<x:main title="The Spy Who Awked Me">
    <h1>
        Hello {{ name ? "World" }}!
    </h1>
</x:main>
    

You can, however, access external scope via the context inside the template or by passing the requisite data in as an attribute with the :val filter, as seen in the example below.

    
        <set name="Bob" />
<x:main name:val="name" title="The Spy Who Awked Me">
    <h1>
        Hello {{ name ? "World" }}!
    </h1>
</x:main>
    

File: resources/tags/main.html

    
        <try>
    <do if:val="children">
        <raw>
            {{ children }}
        </raw>
    </do>
    <do>
        <h1>
            Hello {{ context.name ? "World" }}!
        </h1>
    </do>
</try>
    

Merge Attributes and Fragments

When working with components, it's common to need to add attributes to the underlying element(s) they represent. This is most common for the class attribute, but with HTMX and AlpineJS it can useful for many more examples.

Merge attributes can be used on any <x> style element and end with a : to distinguish them from actual component attributes. The : is stripped and the content is merged into all top-level children's attributes by the same name.

Example component resources/tags/button.html:

    
        <button class="px-4 py-1 rounded-md bg-white/8 text-coral cursor-pointer">
    <raw>
        {{ children ? text }}
    </raw>
</button>
    

Used with merge attributes:

    
        <x:button x-on:click:="alert('Hello Mininim!')" text="Click Me!" />
    

Result:

In addition to components like <x:button>, it's also possible to use the <x> tag with no path. When used like this, it acts as an "invisible" fragment wrapper which is not, itself, inserted in the DOM. Its children, however, will be inserted in its place. Combining this with merge attributes, we can see some immediate utility for easily applying common attributes across multiple elements.

    
        <x class:="bg-white/10 p-4 my-6">
    <p>
        Each of these child paragraphs will have the class attribute.
    </p>
    <p>
        But just as with components, only the top-level children.
    </p>
    <p>
        Not <span>this span tag</span> or any subsequent children.
    </p>
</x>