Core Plugins Components
Core Plugins

Components

Leverage HTML templates across your project.


Overview

Components in Manifest are HTML files representing pages, sections, or other UI templates that can be reused throughout a project. Optimized for performance, components are resolved and rendered in milliseconds at browser runtime, eliminating the need for server-side rendering.


Setup

Components are included in manifest.js with all core plugins, or can be selectively loaded. manifest.json is required to register components.

<!-- Meta -->
<link rel="manifest" href="/manifest.json">

<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.js"></script>

Create Components

Create an HTML file anywhere in your project directory, such as /components/header.html. Its filename must be distinct from other components.

header.html
<header class="w-full p-4 bg-page border-b border-line">
    <strong>Acme</strong>
</header>

Components can have any number of top-level elements, like this component home page:

home.html
<h1>Welcome!</h1>
<section>...</section>
<section>...</section>

All components are rendered as a desendent of the index.html body, and cannot contain their own <body> tag.


Nesting

Provided that a circular reference is not created, components can be recursively nestedโ€”such as a logo placed in a header component.

<header class="row items-center gap-2 w-full p-4 bg-page border-b border-line">
    <x-logo></x-logo>
</header>

Page Head Content

Components can include head content in a <template data-head> tag, which gets appended to the head of any route that includes the component, and removed when the component is no longer rendered.

<template data-head>
    <script>
        console.log('๐Ÿ”„ Component article script loaded');
    </script>
</template>

Check the console for this page and you should see the above log.


Register Components

HTML files need to be declared in manifest.json due to browser security restrictions.

manifest.json
{
    "components": [
        "components/home.html",
        "components/about.html"
    ],
    "preloadedComponents": [
        "components/header.html",
        "components/logo.html"
    ],
    ...
}

Components are registered by custom filepath from the project root in one of two arrays:

  • components is the default array, suitable for components that load on-demand.
  • preloadedComponents is for components that should load in the background if not already used by a current route, reducing their load time on subsequent navigations.

Note that components are cached after first render for the duration of the session.


Apply Instances

Components are applied with <x-filename> tags, where filename is the actual name of the source file. Top-level components are placed in the index.html body:

index.html
123456789101112131415161718192021
<!DOCTYPE html>
<html>
    <head>
        ...
    </head>
    <body>

        <!-- header.html -->
        <x-header></x-header>

        <!-- Page content components like home.html and about.html -->
        <main>
            <x-home x-route="/"></x-home>
            <x-about x-route="about"></x-about>
        </main>

        <!-- footer.html -->
        <x-footer></x-footer>

    </body>
</html>

See the router plugin for more information on conditional rendering with the x-route attribute.


Customizing Instances

In some scenarios it's useful to diverge an instance's content or styles from the source component, such as modifying the header for a special page.

Isolate the Instance

If the instance is used globally like the earlier header example, we'll need to create an isolated version that can be modified without affecting the others. The x-route attribute can be used to show or hide instances based on the current route.

<body>

    <!-- Global header, omitted from the special page -->
    <x-header x-route="!special"></x-header>

    <!-- Specialty page -->
    <x-special-page x-route="special"></x-special-page>

</body>

Parent Modification

Modifying just the parent element can be done immediately by adding any new attribute values to the component tag:

special-page.html
<body>
    <x-header id="specialHeader" class="!bg-slate-700"></x-header>
    ...
</body>

These attribute values will be added to the first top-level element in the component file. If an attribute like class already exists on that element, the new values are appended to it. Appended Tailwind utility classes meant to override others may require an !important variant.


Child Modification

Modifying child elements requires custom attributes that can be referenced in the instance placeholder tag like <x-header colors="..." wordmark="...">.

These custom attributes are created in the component source file using Alpine directives (dynamic attributes) like x-text, x-html, and x-bind, or Manifest's extended directives like x-icon.

Component
<header class="row items-center gap-2 w-full p-4 bg-stone-400 dark:bg-stone-800 text-stone-50" :class="$modify('colors')">
    <span x-icon="$modify('icon')"></span>
    <strong x-text="$modify('wordmark')"></strong>
</header>

Each directive requires a $modify('...') value with the name of the custom attribute to be exposed. This attribute is subsequently available to all of the component's instances:

Instance
<x-header
    colors="!text-white !bg-pink-400 dark:!bg-pink-900"
    icon="lucide:globe"
    wordmark="Universal Exports"
></x-header>

If overrides with custom attribute are not required in every instance, place fallback logic in the component source to ensure optimal presentation.

Component
<!-- Static classes apply if not overriden by the colors attribute -->
<header class="row items-center gap-2 w-full p-4 bg-color-400 dark:bg-color-800 text-color-50" :class="$modify('colors')">

    <!-- Doesn't render if the instance omits the icon attribute -->
    <span x-icon="$modify('icon')" x-show="$modify('icon')"></span>

    <!-- Sets "Acme" as a default value if the instance doesn't include the wordmark attribute -->
    <strong x-text="$modify('wordmark') ?? 'Acme'"></strong>

</header>

Injecting Dynamic Data

Instances can also receive their content from a data source. Simply make the custom attributes dynamic with a leading : and turn the value into a data source reference using $x. In this example, we're referencing an arbitrary content.json source:

<x-header
    :colors="$x.content.specialHeader.colors"
    :icon="$x.content.specialHeader.icon"
    :wordmark="$x.content.specialHeader.wordmark"
></x-header>

Powered by Manifest