Spina Blocks

A plugin for Spina CMS that adds reusable block components. Blocks are independent content units with their own templates and fields that can be assembled into pages.

Installation

Add to your Gemfile:

gem "spina-blocks"

Run:

bundle install
rails db:migrate

Configuration

Theme setup

In your theme initializer, add block templates and categories:

# config/initializers/themes/my_theme.rb
Spina::Theme.register do |theme|
  theme.name = "my_theme"
  theme.title = "My Theme"

  # Enable the blocks plugin
  theme.plugins = ["blocks"]

  # Define all available parts (shared between pages and blocks)
  theme.parts = [
    { name: "headline", title: "Headline", part_type: "Spina::Parts::Line" },
    { name: "body", title: "Body", part_type: "Spina::Parts::Text" },
    { name: "image", title: "Image", part_type: "Spina::Parts::Image" },
    { name: "button_text", title: "Button Text", part_type: "Spina::Parts::Line" },
    { name: "button_url", title: "Button URL", part_type: "Spina::Parts::Line" }
  ]

  # Block categories (for organizing blocks in the library)
  theme.block_categories = [
    { name: "heroes", label: "Heroes" },
    { name: "features", label: "Features" },
    { name: "cta", label: "Call to Action" }
  ]

  # Block templates (which parts each block type uses)
  theme.block_templates = [
    {
      name: "hero",
      title: "Hero Section",
      description: "Full-width hero with headline and image",
      parts: ["headline", "body", "image", "button_text", "button_url"]
    },
    {
      name: "cta_banner",
      title: "CTA Banner",
      description: "Call to action banner",
      parts: ["headline", "body", "button_text", "button_url"]
    }
  ]

  # Page templates (can use BlockCollection or BlockReference parts)
  theme.view_templates = [
    {
      name: "homepage",
      title: "Homepage",
      parts: ["headline", "page_blocks"]
    }
  ]
end

Block view templates

Create partials for each block template:

<%# app/views/my_theme/blocks/_hero.html.erb %>
<section class="hero">
  <h1><%= block_content(block, :headline) %></h1>
  <div><%= block.content(:body) %></div>
</section>

Using blocks on pages

Option 1: Page assembled from blocks (via PageBlocks)

In your page template, render all attached blocks:

<%# app/views/my_theme/pages/homepage.html.erb %>
<%= render_blocks %>

Manage which blocks appear on a page via Admin > Blocks > (select page).

Option 2: Block as a part type

Use Spina::Parts::BlockReference for a single block or Spina::Parts::BlockCollection for multiple blocks in your theme parts:

theme.parts = [
  { name: "hero_block", title: "Hero Block", part_type: "Spina::Parts::BlockReference" },
  { name: "page_blocks", title: "Page Blocks", part_type: "Spina::Parts::BlockCollection" }
]

Then in your template:

<%# Single block reference %>
<%= render_block(content(:hero_block)) %>

<%# Block collection %>
<% content(:page_blocks)&.each do |block| %>
  <%= render_block(block) %>
<% end %>

Models

Model Description
Spina::Blocks::Block Reusable content block with template and parts
Spina::Blocks::Category Block category for organizing the library
Spina::Blocks::PageBlock Join model linking blocks to pages (with position)

Admin interface

The plugin adds:

  • Blocks link in the Content section of the admin sidebar
  • Block library with category tabs for filtering
  • Block editor with content fields (same as page editor)
  • Page Blocks management page (per-page block assignment and ordering)

Helper methods

Helper Description
render_blocks(page) Render all blocks attached to a page via PageBlocks
render_block(block) Render a single block using its template partial
block_content(block, :part_name) Access a block's content
block_has_content?(block, :part_name) Check if a block has content

License

MIT