Installation

1. Clone & Install

git clone <repository-url>
cd homeplate
npm install

2. Start the Server

node server.js

3. Run the Installer

Navigate to http://localhost:3000 and follow the wizard to configure your database, choose a template, and create your admin account.

4. Access Your Site

  • Admin Panel: /admin
  • Public Site: /

Requirements

  • Node.js 14.0.0+
  • MySQL 5.7+ or MariaDB 10.3+
  • npm or yarn

Custom Port

To use a port other than 3000, create a .env file before starting:

echo "PORT=8080" > .env

Template Basics

Templates live in the templates/ directory. Each template is a folder containing HTML files. Homeplate uses a drop-in system — any valid HTML file can become a CMS template.

How It Works

Place special shortcodes in your HTML. The CMS replaces them with content from the database when the page loads. Everything renders server-side, so content appears immediately with no JavaScript required.

<!DOCTYPE html>
<html>
<head>
  <title>{{text:site_name}}</title>
</head>
<body>
  {{include:header.html}}

  <main>
    <h1>{{text:site_name}}</h1>
    {{block:hero_content}}
    {{data:view:featured_products}}
  </main>

  {{include:footer.html}}
</body>
</html>
Full Freedom Templates can use any CSS framework, CDN, font, or JavaScript library. There are no restrictions on what you can load.

Content Blocks

Content blocks are editable regions that editors manage through the admin panel at Content in the sidebar.

How to Use Them

Add a shortcode to your template. The admin panel detects it automatically — no registration needed.

Text Blocks

Returns plain text with all HTML stripped. Use for headings, labels, and short strings.

Syntax

<h1>{{text:site_name}}</h1>
<p>{{text:hero_subtitle}}</p>
<title>{{text:page_title}}</title>

Example

If an editor sets site_name to “My Store”, the output is:

<h1>My Store</h1>

Rich Text Blocks

Returns full HTML content with formatting. Use for body copy, feature sections, and any content that needs rich formatting.

Syntax

<div class="content">
  {{block:about_content}}
</div>

<section>
  {{block:feature_section}}
</section>

Example

If an editor writes rich text with bold, links, and paragraphs, all formatting is preserved in the output.

Includes

Include shared HTML files. Great for headers, footers, and navigation that repeat across pages.

Syntax

<body>
  {{include:header.html}}

  <main>
    <!-- page content -->
  </main>

  {{include:footer.html}}
</body>

The included file is injected inline, so it shares the same scope and styles as the parent template.

Data Blocks & Views

Data blocks are collections of structured content — products, blog posts, team members, testimonials, etc. You define a schema, editors add items, and you display them with views.

Step 1: Create a Data Block

In the admin panel under Data, create a new block and define its fields (title, description, price, images, etc.).

Step 2: Add Items

Editors add records through the admin panel — products, posts, or whatever the collection holds.

Step 3: Create a View

A view defines how items render. You write an HTML template for a single item, and the CMS loops through all matching items.

Data Views

Use the data:view shortcode to render a view in your template.

In Your Template

<section>
  <h2>Featured Products</h2>
  <div class="product-grid">
    {{data:view:featured_products}}
  </div>
</section>

View Template (written in the admin panel)

<div class="product-item">
  <a href="/product/{{data:id}}">
    <div class="product-card">
      {{data:attr:images}}
      <h3>{{data:attr:title}}</h3>
      <p class="price">${{data:attr:price}}</p>
      <p>{{data:attr:description}}</p>
      {{if_sellable}}
        <div class="actions">
          {{shopping:product:quantity}}
          {{shopping:product:add_to_cart}}
        </div>
      {{endif}}
    </div>
  </a>
</div>

Placeholders Available in Views

PlaceholderWhat It Outputs
{{data:id}}The record's ID
{{data:block_name}}The data block name
{{data:block_id}}The data block ID
{{data:attr:fieldname}}Rendered attribute (full HTML for images)
{{data:url:fieldname}}Just the URL (for image/gallery types)
{{data:text:fieldname}}Field value with HTML stripped

Filtering Views

Static Filters (Admin Panel)

When editing a view, set a Filter Config to control which items appear:

{
  "filters": [
    { "attribute": "category", "operator": "=", "value": "Electronics" },
    { "attribute": "price", "operator": "<=", "value": 100 }
  ]
}

Dynamic Filters (in your template)

Pass filters via query string in the shortcode:

{{data:view:products?category=Electronics}}

Operators

OperatorMeaning
=Equals
!=Not equals
> >=Greater than / greater than or equal
< <=Less than / less than or equal
LIKEContains text (case-insensitive)
INValue is in an array
NOT INValue is not in an array

Template Files

config.json

{
  "name": "template-name",
  "title": "Template Name",
  "description": "A description of your template",
  "version": "1.0.0",
  "author": "Your Name"
}

Page Files

FilePurpose
index.htmlHomepage (required)
store.htmlProduct listing
product.htmlSingle product page
cart.htmlShopping cart
checkout.htmlCheckout
order-confirmation.htmlOrder confirmation
blog.htmlBlog listing
blog-post.htmlBlog post
cms-page.htmlAdmin-created custom pages (uses {{page:content}})
404.htmlError page
privacy.htmlPrivacy policy
terms.htmlTerms of service

Setting Up a Store

1. Create a Product Collection

In Data, create a new data block with fields like:

title       text        required
price       number      required
description textarea
images      gallery
category    text
sku         text
featured    boolean
sale_price  number

2. Enable Sellable

Check "Is Sellable" on the data block and set the cart config:

{
  "price_attribute": "price",
  "discounted_price_attribute": "sale_price",
  "title_attribute": "title",
  "description_attribute": "description",
  "image_attribute": "images",
  "category_attribute": "category",
  "sku_attribute": "sku"
}

3. Create Views

Create views for different displays: products_list, products_detail, featured_products, etc.

4. Store Routes

RouteTemplate
/storestore.html
/product/:idproduct.html
/cartcart.html
/checkoutcheckout.html
/order-confirmation/:orderNumberorder-confirmation.html

Shopping Tags

Product Tags

TagOutput
{{shopping:product:add_to_cart}}Add to cart button
{{shopping:product:quantity}}Quantity selector with +/- buttons
{{shopping:product:price}}Formatted price (with discount if applicable)
{{shopping:products:list}}All products from every sellable block
{{shopping:products:featured}}Products with featured = true
{{shopping:products:sale}}Products with on_sale = true

Cart Tags

TagOutput
{{shopping:cart:items}}Cart items list with images, titles, quantities, and remove buttons
{{shopping:cart:summary}}Cart totals: subtotal, tax, total with checkout link
{{shopping:cart:count}}Total quantity of all items in cart
{{shopping:cart:view}}Link to cart page
{{shopping:cart:subtotal}}Formatted subtotal (before tax) as <span class="cart-subtotal">
{{shopping:cart:total}}Formatted grand total (subtotal + tax) as <span class="cart-grand-total">
{{shopping:cart:item_count}}Number of distinct line items (not total quantity) as <span class="cart-item-count">

Checkout

TagOutput
{{shopping:checkout:form}}Full checkout form

Auto-Injected Checkout Assets

These assets are automatically injected into checkout.html. No manual <script> tags needed.

AssetDescription
/css/store.cssDefault store styling (overridable via CSS custom properties)
/cart-handler.jsCart add / remove / update functionality
/states.jsUS state dropdown data — auto-populates <select id="state">

Conditionals

{{if_sellable}}
  <p>Price: {{shopping:product:price}}</p>
  {{shopping:product:add_to_cart}}
{{endif}}

{{if_not_sellable}}
  <p>Contact for pricing</p>
{{endif_not_sellable}}

Template Variables

Global

VariableValue
{{text:site_name}}Site name from settings
{{text:site_description}}Site description
{{text:site_url}}Site URL

Attribute Types

When you define a schema for a data block, each field has a type. Here is how each type renders when used with {{data:attr:fieldname}}:

TypeRendering
textPlain text string
textareaMulti-line text
numberNumeric value
booleanYes / No
imageAn <img> tag
galleryFirst image as <img>; slideshow starts automatically for multiple images
selectSelected option value
urlURL string
dateFormatted date

Cart CSS Classes

Use these classes in your templates for cart functionality. The JS handler binds to them automatically via event delegation — no inline onclick handlers needed.

Buttons

ClassDescription
.cart-add-btnAdd to cart button
.cart-add-buttonAdd to cart button (CartService variant)
.cart-remove-btnRemove item from cart button
.cart-clear-btnClear all items button
.cart-checkout-btnProceed to checkout button
.cart-continue-btnContinue shopping button
.cart-view-product-btnView Product link (for items with variants)
.cart-out-of-stock-btnOut of Stock disabled button

Display

ClassDescription
.cart-containerMain cart container
.cart-itemIndividual cart item row
.cart-item-imageProduct image in cart
.cart-item-detailsProduct details container
.cart-item-titleProduct title
.cart-item-typeProduct type / category
.cart-item-priceProduct price
.cart-item-quantityQuantity controls container
.cart-summaryCart summary section
.cart-subtotalCart subtotal value (before tax)
.cart-totalCart total element (legacy — renders subtotal)
.cart-grand-totalCart grand total value (subtotal + tax)
.cart-item-countNumber of distinct line items in cart
.cart-emptyEmpty cart message

Quantity

ClassDescription
.cart-quantity-inputQuantity number input
.cart-quantity-selectorQuantity controls wrapper
.cart-quantity-decreaseDecrease quantity button
.cart-quantity-increaseIncrease quantity button

Header Cart

ClassDescription
.cart-btnCart button in header
.cart-btn-iconCart icon SVG
.cart-badgeCart item count badge

Product Cards

ClassDescription
.product-cardProduct card wrapper
.product-imageProduct image
.product-titleProduct title heading
.product-priceProduct price display
.product-descriptionProduct description text
.product-detailsCard details wrapper
.product-category-badgeCategory label
.product-sale-badgeSale / discount badge
.product-variantsVariant selector wrapper
.variant-selectVariant dropdown or text input
.product-actionsAdd-to-cart area on detail page
.breadcrumbProduct detail breadcrumb nav

Checkout Form

ClassDescription
.checkout-inputAll checkout form inputs
.checkout-checkboxCreate account checkbox
.checkout-submit-btnPay Now submit button
.checkout-sectionCheckout form section card
.checkout-gridTwo-column checkout layout
.checkout-login-inputLogin prompt email / password inputs
.checkout-login-btnLogin prompt submit button
.checkout-login-toggle-btnToggle login form button
.checkout-promo-inputPromo code text input
.checkout-promo-apply-btnPromo code Apply button
.checkout-promo-remove-btnPromo code Remove button
.checkout-state-selectState / province dropdown select
.payment-method-btnPayment method selector button
.payment-containerPayment method content container
.filter-btnCategory filter button
.filter-buttonsCategory filter button group

Data Attributes

AttributeDescription
data-block-idData block ID for product
data-record-idData record ID for product
data-cart-item-idCart item ID
data-cart-countCart item count (for badges)

Example

<!-- Add to cart button -->
<button class="cart-add-btn" data-block-id="123" data-record-id="456">
  Add to Cart
</button>

<!-- Cart item display -->
<div class="cart-item" data-cart-item-id="789">
  <img class="cart-item-image" src="product.jpg" alt="Product">
  <div class="cart-item-details">
    <div class="cart-item-title">Product Name</div>
    <div class="cart-item-price">$19.99</div>
  </div>
  <div class="cart-item-quantity">
    <input class="cart-quantity-input" data-cart-item-id="789"
           value="1" min="1" max="99">
    <button class="cart-remove-btn" data-cart-item-id="789">Remove</button>
  </div>
</div>

Blog Shortcodes

Blog Listing (blog.html)

ShortcodeOutput
{{blog:posts}}List of published blog posts
{{blog:pagination}}Previous / next pagination controls

Blog Post (blog-post.html)

ShortcodeOutput
{{blog:post_title}}Post title
{{blog:post_content}}Post body (HTML)
{{blog:post_date}}Formatted publish date
{{blog:post_slug}}URL slug
{{blog:post_image}}Post image URL
{{blog:post_image_meta}}<meta property="og:image"> tag (conditional)
{{blog:post_image_meta_twitter}}<meta name="twitter:image"> tag (conditional)
{{blog:post_meta_description}}Meta description text
{{blog:post_url}}Full URL to the post
{{blog:related_posts}}Up to 3 related posts sharing tags

Custom Pages (CMS)

Admins can create pages in the admin panel under Pages. These pages are stored in the database and rendered using cms-page.html.

Page Shortcodes

ShortcodeOutput
{{page:title}}Page title
{{page:content}}Page body (HTML)
{{page:slug}}URL slug
{{page:meta_title}}SEO meta title (falls back to title)
{{page:meta_description}}Meta description text
{{page:image}}Page image URL
{{page:url}}Full URL to the page
{{page:image_meta}}OG image meta tag (conditional)
{{page:image_meta_twitter}}Twitter image meta tag (conditional)

System Pages vs Custom Pages

TypeSourceEditable
System PagesTemplate HTML files (e.g. about.html)Edit template file directly
Custom PagesDatabase (created in admin)Yes — via admin panel

Menu Shortcodes

Navigation menus are managed by admins under Pages > Menu Manager with drag-and-drop reordering.

ShortcodeOutput
{{menu:items}}Renders <nav> with all active menu items
{{menu:items_json}}JSON array of menu items (for JS-driven navs)

Customer Accounts

Homeplate includes a built-in customer portal at /customer/ where customers can create accounts, view order history, pay invoices, and manage their profile.

Required Pages

PagePurpose
checkout.htmlCheckout form with optional account creation
order-confirmation.htmlOrder success page
order-history.htmlCustomer order history

Order Payment Statuses

StatusMeaning
pendingAwaiting payment
paidPayment completed
failedPayment failed
refundedPayment refunded