No release in over 3 years
not_pressed-core is the foundational engine behind NotPressed, a fully open-source, batteries-included CMS for Rails 8. It provides hierarchical pages, a block-based content editor, automatic navigation discovery from routes and view partials, a media library backed by Active Storage, a pluggable admin panel with configurable auth, and a content type registry — all built on Hotwire with zero external JS dependencies.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 3.0
~> 8.0
~> 2.3
 Project Readme

not_pressed-core

The core engine behind NotPressed — a drop-in, fully open-source CMS for Rails 8. Auto-discovers your app's navigation, provides a block-based content editor, and supports plugins and themes — all without the WordPress tax.

Not WordPress. Not pressed for cash. Not pressed for time.

Why not_pressed-core?

Most CMS options force you to either adopt a monolithic platform (WordPress, Strapi) or bolt on a headless CMS that doesn't understand your Rails app. not_pressed-core takes a different approach: it's a Rails engine that lives inside your existing application, works with your routes and views, and stays out of your way.

  • 3 commands to a working CMS — install the gem, run the generator, migrate
  • Works with your app — auto-discovers routes, partials, and navigation from your existing codebase
  • Zero external JS — built entirely on Hotwire, Turbo, and Stimulus
  • Batteries included — pages, blog, media library, block editor, forms, SEO, themes, plugin system
  • Extensible — plugin architecture, custom content types, theme system, import/export

Requirements

  • Ruby >= 3.2
  • Rails >= 8.0
  • Active Storage (for media library)

Installation

Add not_pressed-core to your Gemfile:

gem "not_pressed-core"

Run the install generator:

bundle install
rails generate not_pressed:install
rails db:migrate

Optionally load sample content:

rails runner "load 'db/seeds/not_pressed.rb'"

Visit /not_pressed to access the admin panel.

Features

Block-Based Page Editor

Build pages from ordered, typed content blocks with a drag-and-drop editor:

Block Type Description
text Rich text paragraph
heading Section heading (h1-h6)
image Image with caption and alt text
video Embedded video
code Syntax-highlighted code block
quote Blockquote with attribution
divider Visual separator
html Raw HTML injection
form Embedded form (from form builder)
gallery Image grid with lightbox viewer
callout Highlighted callout box (plugin)

Custom block types can be added via the plugin system.

Page Management

  • Hierarchical page tree with drag-to-reorder
  • Draft / published / scheduled / archived workflow
  • Page duplication, version history
  • SEO fields, layout selection, visibility controls
  • Live preview with split/toggle pane

Blog

Built-in blog with categories, tags, and RSS:

  • Blog posts are pages with the blog_post content type
  • Category and tag management in admin
  • Public blog listing with pagination
  • Category and tag filtering
  • Year/month archives
  • RSS 2.0 feed at /blog/feed.rss

Media Library

Centralized media management backed by Active Storage:

  • Upload, browse, search, and filter
  • Automatic metadata extraction (dimensions, file size, content type)
  • Alt text and title fields for accessibility and SEO
  • Image variants and thumbnail generation
  • Media picker modal for the block editor

Forms

Drag-and-drop form builder with submissions:

  • Field types: text, email, textarea, select, checkbox, radio, number, date, phone, url
  • Submission management with CSV export
  • Email notifications on submission
  • Embeddable via the form block type

SEO & Discoverability

  • Per-page meta title, description, Open Graph, Twitter Cards
  • Canonical URLs and robots meta directives
  • Auto-generated XML sitemap at /sitemap.xml
  • Configurable robots.txt
  • Per-page and global JS/CSS code injection

Navigation

not_pressed-core builds navigation automatically using three layers:

  1. Config DSL — explicitly defined menus
  2. Route introspection — scans your Rails routes for navigable pages
  3. Partial parsing — detects existing nav/menu/header partials in your views

Configuration

The install generator creates config/initializers/not_pressed.rb:

NotPressed.configure do |config|
  # Site name displayed in the admin header
  config.site_name = "My Site"

  # Admin panel mount path (default: "not_pressed")
  config.admin_path = "admin"

  # Records per page in admin listings
  config.per_page = 25

  # Authentication — Devise method, custom Proc, or nil (no auth)
  config.admin_authentication = :authenticate_admin_user!
  config.admin_current_user = :current_admin_user
  config.admin_unauthorized_redirect = "/login"

  # Available layouts for pages
  config.available_layouts = %w[default sidebar full_width]

  # Default theme
  config.default_theme = "Starter"

  # Auto-discover navigation from routes and view partials
  config.auto_discover_navigation = true
  config.cache_navigation = true

  # SEO title separator
  config.seo_title_separator = " | "

  # Global code injection (head/body)
  config.global_head_code = ""
  config.global_body_code = ""

  # Navigation DSL
  config.menus do |menu|
    menu.add "Home", "/"
    menu.add "Blog", "/blog"
    menu.add "About", "/about"
  end
end

Authentication

not_pressed-core doesn't ship its own auth — it plugs into yours:

# Devise
config.admin_authentication = :authenticate_admin_user!

# Custom logic
config.admin_authentication = ->(controller) {
  controller.redirect_to "/login" unless controller.session[:admin]
}

# No auth (development only!)
config.admin_authentication = nil

Extending NotPressed

Custom Content Types

Define custom content types with the builder DSL:

NotPressed.define_content_type(:event) do
  label "Event"
  icon "calendar"
  description "An event with date and location"
  allowed_blocks :text, :heading, :image
  has_slug true
  has_parent false

  field :event_date, :datetime, label: "Event Date", required: true
  field :venue, :string, label: "Venue"
  field :ticket_url, :url, label: "Ticket URL"
end

Plugins

WordPress-style plugin system with hooks, filters, custom blocks, and admin extensions:

class MyPlugin < NotPressed::Plugin
  name "my_plugin"
  version "1.0.0"
  description "Does something useful"

  on(:after_page_save) { |page| Rails.logger.info "Saved: #{page.title}" }

  block_type :callout

  settings do
    field :api_key, :string, label: "API Key"
  end

  admin_menu label: "My Plugin", icon: "puzzle-piece"

  routes do
    get "my-plugin", to: "my_plugin#index"
  end
end

Plugins can be exported as .zip archives and imported on other installations via the admin panel.

See Plugin Development Guide for full documentation.

Themes

Swappable themes with layouts, color schemes, and template overrides:

class MyTheme < NotPressed::Theme
  name "my_theme"
  version "1.0.0"

  layout :default, label: "Default", primary: true
  layout :sidebar, label: "With Sidebar"

  stylesheet "not_pressed/themes/my_theme"

  color_scheme do
    color :primary, "#3366cc", label: "Primary"
    color :background, "#ffffff", label: "Background"
    color :text, "#333333", label: "Text"
  end
end

Themes support color customization from the admin panel, template overrides via view path prepending, and can be exported/imported as .zip archives.

See Theme Development Guide for full documentation.

Development

After checking out the repo:

bundle install
cd spec/dummy && rails db:create db:migrate && cd ../..
bundle exec rspec

The test suite uses a Rails 8 dummy app in spec/dummy/.

Documentation

License

not_pressed-core is released under the NPL-1.0 License.