🚀 Andalina is evolving fast! Star or Watch our repository to get instantly notified about new features and releases.

Documentation

What is andalina?

andalina is a zero-dependency, development-time composition tool designed to enforce the DRY (Don't Repeat Yourself) principle for HTML authoring. It is not a framework for serving web applications; it is a template parser for HTML/XHTML that runs directly in the browser.

The Problem We Solve:

This addresses a classic Developer Experience (DX) problem: the handoff friction between Front-End (FE) and Back-End (BE) teams. Often, when the FE team delivers raw HTML, the BE team has to rewrite or "clean up" the HTML to ingest it or wrap it in their server-side logic (JSF, ASP, Laravel, PHP, Django, JSP, Thymeleaf, Blade, Jinja2, etc.). This leads to massive BE/FE misalignment during the development phase.

The Vision:

Our vision is to solve this misalignment by providing a simple, unified, language-agnostic template language. By using andalina, the FE team delivers structured, modular components that perfectly map to server-side component boundaries. The BE team can now easily integrate these templates without rewriting the HTML. This vision is built upon four core pillars:

  • Zero-Dependency: andalina runs entirely in the browser. No Node.js build pipelines, no Webpack, and no server-side rendering required.
  • Simple & Easy to Learn: andalina uses only 5 core tags. It is incredibly easy to pick up, even for absolute beginners.
  • Standard HTML/XHTML: There is no need to learn a new programming language or complex framework. It's just the HTML you already know!
  • Single Source of Truth: Front-End developers can fix bugs in exactly one place. No more repeating code across 20 files—andalina handles the repetition for you.

How to Use

1. Download Andalina

First, download the latest version of Andalina from the Download Page (or our GitHub Repository). The downloadable archive (.zip) includes:

  • andalina.js: The core zero-dependency client-side template engine.
  • andalina.config.json: Mandatory configuration file for defining paths and framework-safe data binding delimiters.

2. Add to Your HTML Document

Place andalina.js in your project directory and simply drop the script tag into the <head> of your HTML document. Ensure you include the defer attribute so Andalina executes after the DOM is ready:

3. Run a Local Development Server

Important Note: Because andalina uses the Fetch API to load templates dynamically, modern browsers will block it due to CORS restrictions if you open the HTML/XHTML file directly (e.g., file://). You must run your project through a local development server (like VS Code's "Live Server" extension) for andalina to work!

<an-include>

Used to inject static HTML partials directly into your page.

Attributes:

  • src (Optional): The exact path to the HTML/XHTML file you want to include.
  • name (Optional): The name of the include fragment (resolves to [includesPath]/[name][extension]).
    Note: You must provide either src or name.

Example:

1. The Partial (head-content.html)

2. The Call (index.html)

Rendered HTML:


<an-data>

Used to fetch external JSON data and store it in memory for your templates to use.

Attributes:

  • src (Required): The URL or path to the JSON endpoint/file.
  • name (Required): The variable name to store the fetched data in.

Example:

Example 2 (Global Data Binding & Arrays):

Any data fetched using <an-data> becomes globally available to the entire document. You can inject specific strings or nested objects directly into your HTML or component attributes anywhere on the page, without needing a loop!

Rendered HTML (Example 2):

<an-repeat>

Used to loop content dynamically. You can either repeat a specific number of times, or iterate over data fetched with <an-data>.

Attributes:

  • times (Optional): An integer specifying how many times to repeat the content block. (Used for simple numeric loops).
  • data (Optional): The name of the data array fetched via <an-data> to iterate over.
  • item (Optional, Default: "item"): The local variable name used to access the current object in the data array.
  • index-as (Optional, Default: "$index"): The variable name used to access the current loop iteration index.

Example 1 (Simple numeric loop):

Rendered HTML (Example 1):

Example 2 (Looping over fetched data):

Rendered HTML (Example 2):

Architecture Guide: <an-layout> vs <an-component>

When structuring your application, choosing between an <an-layout> and an <an-component> follows a clean separation of concerns:

Concept <an-layout> <an-component>
Primary Role Content Holder & Structural Organizer Discrete, Customizable UI Block
Purpose Frames, positions, and organizes dynamic content. Self-contained, bounded UI element with a defined shape.
Primary Mechanism Slots (<an-place> & <an-inject>) Attributes & Props (<an-attributes> & dynamic props)
Directory layouts/ (layoutsPath) components/ (componentsPath)
Typical Examples Page shells, grid columns, sections, sidebars, accordions, tabs, dialog containers Buttons, badges, form inputs, toggles, tab-headers, avatars, stat chips
Rule of Thumb: If its primary job is to hold, arrange, and frame other content (such as a Grid, Accordion, Sidebar, or Section container), make it an <an-layout>. If it is a discrete, reusable building block configured by attributes (like a Button, Input, or Badge), make it an <an-component>.

<an-component>

Used to instantiate reusable, self-contained UI building blocks (buttons, badges, inputs, toggles, cards, avatars) and configure them via attributes/props. Variables inside the component are defined using <an-attribute>.

Attributes:

  • src (Optional): The exact path to the component HTML/XHTML file.
  • name (Optional): The name of the component (resolves to [componentsPath]/[name][extension]).
    Note: You must provide either src or name.
  • Dynamic Attributes: Any other attribute you add will be passed to the component as a property.

Inside the component file, define your component using Andalina's Structured Syntax: wrap the file in an <an-component-def> container, specify metadata in an <an-attributes> block, and place your markup inside an <an-body> block.

Inside <an-attributes>, use <an-attribute> to define the props. It accepts:

  • name (Required): The name of the property.
  • default-value (Optional): A fallback value if the prop is not passed.
  • mandatory (Optional): Set to "true" to log a warning if the prop is missing.

Example 1: Prop-Driven Component (user-card.html)

1. The Component (user-card.html)

2. The Call (index.html)

Rendered HTML:

Example 2: Slot-Injected Component (custom-btn.html)

While components are predominantly configured via attributes and props, they can also define internal <an-place> slots for cases requiring rich markup injection (such as custom button icons or action bars). Callers populate these slots using <an-inject>:

1. The Component (custom-btn.html)

2. The Call (index.html)

Strict Slot Scoping: In Andalina v1.8.1+, <an-inject> tags are strictly scoped to their immediate parent <an-component>, preventing nested slots from colliding.

<an-layout>

The Content Holder & Structural Organizer of your application. Layouts provide reusable structural shells (like dashboard grids, sidebar scaffolding, two-column form sections, accordions/tabs, or card containers) and can be nested inside <an-template> or within another layout.

Layouts use <an-place> slots to accept arbitrary content injected by the caller via <an-inject>. Layouts can also accept configuration attributes via <an-attributes>.

Like components, layouts use Andalina's Structured Syntax: wrap the file in an <an-layout-def> container, specify optional metadata in an <an-attributes> block, and place your structural markup inside an <an-body> block.

Note on Nested Slots: In Andalina v1.8.1+, slot injections (<an-inject>) are strictly scoped to their immediate parent container. If a page or layout nests a child component that also defines an inner slot with the same name (e.g. name="content"), the inner slot will never collide with or overwrite the parent's content.

Attributes:

  • src (Optional): The exact path to the layout HTML/XHTML file.
  • name (Optional): The name of the layout (resolves to [layoutsPath]/[name][extension]).
    Note: You must provide either src or name.

Example:

1. The Grid Layout (dashboard-layout.html)

2. The Calling Page (admin.html)

Rendered HTML:

<an-template>

The root tag used in your final page to wrap your content inside a complete HTML/XHTML document template. Unlike an <an-layout> (which acts as a structural partial), a template provides the full, standard structure of a webpage, including the <html>, <head>, and <body> tags.

Attributes:

  • src (Optional): The exact path to the template page HTML/XHTML file.
  • name (Optional): The name of the template page (resolves to [templatesPath]/[name][extension]).
    Note: You must provide either src or name.

Example:

1. The Root Template (master.html)

2. The Calling Page (index.html)

Rendered HTML:

<an-code>

Used to inject and safely escape source code inside documentation blocks—either by fetching an external file (src="js/app.js") or by extracting a live element directly from the current page (src="#element-id").

Syntax Highlighting Warning: If you are using a code highlighter like highlight.js or Prism.js, you must ensure it only runs after your <an-code> blocks have been fully fetched and injected. For a detailed explanation of this async race condition and how to fix it, please see the Third-Party JS Compatibility section.

Note on Dev-Server Scripts: When loading component or template files, any live-reloading scripts injected by local development servers (e.g. VS Code Live Server WebSocket reloaders) are automatically stripped so they never appear inside your code boxes.

Attributes:

  • src (Required):
    • File Path (e.g. src="js/app.js"): Fetches the external source file (relative to codesPath if configured). External files are injected as-is without formatting by default to preserve language-specific indentation (Java, Python, XML, JS, etc.).
    • In-Page Element Selector (e.g. src="#button-demo"): Clones the matching element from the current page, strips its demo id attribute by default, and auto-formats the HTML indentation.
  • stage (Optional, default: "after"): Controls when an in-page element (src="#id") is captured:
    • stage="before": Captures the raw, unrendered Andalina markup (e.g., <an-component>, <an-repeat>, {{ prop }}) before Andalina processes it.
    • stage="after": Captures the final rendered HTML after all Andalina components, loops, conditionals, and data bindings have resolved.
  • unwrap (Optional, default: "false"): When set to "true", extracts only the inner content (innerHTML) of the target wrapper element instead of including the wrapper tag itself (outerHTML).
  • keep-id (Optional, default: "false"): By default, Andalina strips the id attribute from the cloned root element so demo selector IDs do not clutter your documentation snippets. Set keep-id="true" to preserve the id attribute.
  • format (Optional): Controls HTML auto-formatting ("true" or "false"). Defaults to "true" for in-page elements (src="#id") and "false" for external files.

Example 1: Loading Code from an External File

1. The Source Code (js/app.js)

2. The Call (index.html)

Example 2: Extracting Live In-Page Demo Code (Two-Tab Pattern)

You can point multiple <an-code> tags to the same demo container using stage="before" and stage="after" to showcase both the Andalina component call and the final compiled HTML output:

<!-- 1. Live Demo Container -->
<div id="button-demo">
   <an-component name="primary-button" label="Save Changes"></an-component>
</div>

<!-- 2. Tab 1: Show Raw Andalina Code (Pre-Engine) -->
<pre><code class="language-html"><an-code src="#button-demo" stage="before" unwrap="true"></an-code></code></pre>

<!-- 3. Tab 2: Show Final Rendered HTML (Post-Engine) -->
<pre><code class="language-html"><an-code src="#button-demo" stage="after" unwrap="true"></an-code></code></pre>

<an-if> / <an-else>

Evaluate a conditional block. You can use native JavaScript expressions inside the condition against your <an-data> sources.

Attributes:

  • condition (Required on an-if): The JavaScript expression to evaluate. Must resolve to a truthy or falsy value.

Example:

<an-if condition="store.isOpen && user.name !== ''">
   <p>Welcome to the store!</p>
</an-if>
<an-else>
   <p>Sorry, we are closed.</p>
</an-else>

JS Expressions in Bindings

In Andalina v1.6.0+, standard double-curly bindings fully support native JavaScript expressions, array methods, and fallbacks.

<!-- Fallbacks -->
<p>Welcome, {{ user.name || 'Guest' }}!</p>

<!-- Math & Logic -->
<p>Total Price with Tax: {{ price * 1.2 }}</p>
<p>Discount: {{ price > 100 ? '20%' : '5%' }}</p>

<!-- an-comment: -->

Andalina provides a specialized HTML comment syntax for development notes that are automatically stripped out when you compile your project using the Andalina Builder.

Syntax:

<!-- an-comment: Your note here -->

Example:

<!-- an-comment: This is a note for developers that won't appear in the final HTML -->
<div>Visible Content</div>

Configuration

Define the global behavior in andalina.config.json:

Properties:

  • showRenderedHtml: {enabled, disabled}. Enables or disables the output of the final rendered HTML into the Developer Tools Console.
  • debug: {enabled, disabled}. Enables or disables the output of detailed debugging information during the rendering process.
  • debugUI: {true, false}. When debug is enabled, this toggles the floating visual component tree overlay. Defaults to true.
  • preventFOUC: {enabled, disabled}. Prevents Flash of Unstyled Content by hiding the page body until rendering completes.
  • propStart: (Mandatory) The starting string for properties (e.g. {{).
  • propEnd: (Mandatory) The ending string for properties (e.g. }}).
  • prefix: A custom prefix for Andalina tags. Defaults to an. It can be any string (e.g., an, andalina, aa).
  • componentsPath: The directory path where your component files are stored.
  • layoutsPath: The directory path where your layout files are stored.
  • templatesPath: The directory path where your templates files are stored.
  • includesPath: The directory path where your HTML fragment and include files are stored.
  • codesPath: The directory path where your source code snippet files are stored.
  • extension: The default file extension to be used for components, layouts, pages, and includes when referring to them by name only. It can be any string, but it must start with a dot (e.g., .html, .xhtml).

Example:

Page Scope Configuration: You can override the global configuration directly on a specific page by adding data attributes to the andalina script tag.

Show Rendered HTML

Since andalina runs client-side and dynamically manipulates the DOM, viewing the page source in your browser (Right Click > View Page Source) will only show your <an-> tags, not the final rendered HTML.

If you need to inspect or copy the completely rendered HTML, you can enable the showRenderedHtml configuration globally in andalina.config.json, or on a single page using data-show-rendered-html="enabled":

When enabled, andalina will print the Final Rendered HTML directly into your Developer Tools Console! You can easily inspect it or copy-paste it.

Debugging

When working with complex nested templates, it's easy to lose track of what andalina is fetching. To help with this, you can enable debug mode.

Enable Debug Mode:

You can turn on debug mode globally in your andalina.config.json, or on a single page using the script attribute: data-debug="enabled".

When enabled, andalina will print a beautiful, structured trace directly to your browser's Developer Tools Console, showing exactly which files were fetched, how long the network request took, and the properties passed to each component.

Third-Party JS Compatibility

Because andalina dynamically fetches and injects HTML into the DOM asynchronously, it can cause race conditions with third-party JavaScript libraries (such as sliders, lightboxes, or UI frameworks) that attempt to scan the DOM immediately on page load.

The Issue: If a third-party library runs before andalina has finished injecting your components, the library won't find the elements and will fail to initialize.

The Solution:

You must initialize your third-party libraries after you are sure the asynchronous fetching has completed. A reliable way to handle this is by awaiting the component injections or using a Promise.all approach if you are manually fetching content.

Example: Safe Initialization

Andalina Builder

The Andalina Builder is a powerful VS Code Extension that performs AOT (Ahead of Time) Transpilation. It seamlessly converts your raw Andalina project directly into static SSG files or native Enterprise framework views.

Features:

  • GUI Configuration: Manage your build targets natively in VS Code without editing JSON files manually.
  • Multi-Target Transpilation: With a single click, convert your Andalina UI into:
    • Static HTML (Flat SSG)
    • JavaServer Faces (JSF) [Experimental] (Outputs a Maven Web App structure with Composite Components and Facelets Templating)
    • Laravel Blade [Experimental] (Uses @component, @slot, @extends)
    • Django [Experimental] (Uses {% include %}, {% block %}, {% extends %})
    • Thymeleaf [Experimental] (Uses th:replace, th:fragment, th:each)
  • Advanced Architecture Support: Resolves <an-component>, <an-layout>, <an-template>, and <an-inject> tags directly into native server-side equivalents.
  • Production Cleanup: Safely strips Andalina-specific scripts, styles, <an-data>, and <!-- an-comment --> tags from your final production artifacts.
  • Asset Copying: Automatically copies your images, CSS, and JS files to the target directory.
  • Auto Build on Save: Optionally watch your source files and automatically rebuild when changes are detected.

How to Use:

1. Install the Andalina Builder extension in VS Code.
2. Look for the Andalina Build view in your VS Code Explorer sidebar.
3. Click the + icon to add a new Action.
4. Specify your Source Folder and Target Folder.
5. Check off one or more Target Platforms (e.g. Static HTML, JSF, Blade, etc.).
6. Click the play button on your Action in the sidebar to build your artifacts!