ActiveCanvas
A mountable Rails engine that turns any Rails app into a full-featured CMS. Includes a visual drag-and-drop editor (GrapeJS), AI-powered content generation, Tailwind CSS compilation, media management, page versioning, and SEO controls -- all behind an admin interface that works out of the box.
Features
- Visual Editor -- Drag-and-drop page builder powered by GrapeJS
- AI Content Generation -- Text, images, and screenshot-to-code via OpenAI, Anthropic, or OpenRouter
- Tailwind CSS Compilation -- Per-page compiled CSS for production (no CDN dependency)
- Media Library -- Upload and manage images/files with Active Storage
- Page Versioning -- Automatic version history with diffs and rollback
- Header & Footer Partials -- Reusable components, togglable per page
- SEO -- Meta tags, Open Graph, Twitter Cards, JSON-LD structured data
- Page Types -- Categorize pages (blog posts, landing pages, etc.)
- Authentication -- Pluggable auth (Devise, custom, or HTTP Basic)
- Isolated Namespace -- No conflicts with your host application
Requirements
- Ruby 3.1+
- Rails 8.0+
Installation
Add to your Gemfile:
gem "active_canvas"Run the install generator:
bundle install
bin/rails generate active_canvas:installThe interactive installer will:
- Copy and run database migrations
- Create
config/initializers/active_canvas.rbwith configuration options - Mount the engine in your routes (default:
/canvas) - Prompt you to choose a CSS framework (Tailwind, Bootstrap 5, or none)
- Optionally configure AI API keys
Then visit /canvas/admin to start building pages.
Quick Start
- Go to
/canvas/admin - Create a Page Type (e.g., "Landing Page")
- Create a Page, then click Editor to open the visual builder
- Drag blocks, use AI to generate content, upload images
- Publish the page -- it's live at
/canvas/your-slug
Visual Editor
The GrapeJS editor provides:
- Drag-and-drop blocks (text, images, columns, forms, etc.)
- Code editor panel for direct HTML/CSS editing
- Asset manager integrated with the media library
- AI assistant panel for content generation
- Component-level AI toolbar (edit, rewrite, expand)
- Auto-save (configurable interval, default: 60s)
AI Integration
ActiveCanvas uses RubyLLM to provide AI features directly in the editor.
Capabilities
| Feature | Description | Supported Models |
|---|---|---|
| Chat | Generate and edit HTML content with streaming | GPT-4o, Claude Sonnet 4, Claude 3.5 Haiku |
| Image Generation | Create images from text prompts | DALL-E 3, GPT Image 1 |
| Screenshot to Code | Upload a screenshot, get HTML/CSS | GPT-4o, Claude Sonnet 4 (vision models) |
Setup
Add your API keys via environment variables or Rails credentials:
# config/initializers/active_canvas.rb
Rails.application.config.after_initialize do
# Via environment variables
ActiveCanvas::Setting.ai_openai_api_key = ENV["OPENAI_API_KEY"]
ActiveCanvas::Setting.ai_anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
ActiveCanvas::Setting.ai_openrouter_api_key = ENV["OPENROUTER_API_KEY"]
# Or via Rails credentials
credentials = Rails.application.credentials.active_canvas || {}
ActiveCanvas::Setting.ai_openai_api_key = credentials[:openai_api_key]
endYou can also configure API keys from the admin UI at /canvas/admin/settings (AI tab).
Once configured, sync available models:
bin/rails active_canvas:sync_modelsOr use the Sync Models button in admin settings.
Server Timeout
AI requests (especially image generation and screenshot-to-code) can take longer than typical web requests. If you're running Puma in clustered mode (multiple workers), increase the worker timeout to avoid requests being killed mid-flight:
# config/puma.rb
worker_timeout 180Or via environment variable:
PUMA_WORKER_TIMEOUT=180If you're behind a reverse proxy (Nginx, Apache), also increase its read timeout for the ActiveCanvas routes:
location /canvas {
proxy_read_timeout 180s;
proxy_send_timeout 180s;
}Tailwind CSS Compilation
ActiveCanvas can compile Tailwind CSS at runtime so your public pages don't need the Tailwind CDN.
How it works
- In the editor: Uses Tailwind CDN for instant live preview
- On save: Compiles only the CSS classes used on that page
- Public pages: Serves compiled CSS inline (fast, no CDN)
Setup
Add the tailwindcss-ruby gem (optional -- falls back to CDN if missing):
gem "tailwindcss-ruby", ">= 4.0"Select "Tailwind CSS" as the CSS framework in your initializer or admin settings. That's it -- CSS compiles automatically when you save pages.
You can customize the Tailwind theme (colors, fonts) from admin settings, and trigger a bulk recompile of all pages when needed.
Media Library
Upload and manage images directly from the admin or from within the editor's asset manager.
- Supports JPEG, PNG, GIF, WebP, AVIF, and PDF
- SVG uploads available (disabled by default for security)
- Configurable max file size (default: 10MB)
- Works with any Active Storage backend (local, S3, GCS, etc.)
- Public or signed URL modes
Page Versioning
Every content change creates a version automatically. View the version history from the page admin to see:
- What changed (before/after diffs)
- Who made the change
- When it was made
- Content size differences
Configure the maximum versions kept per page (default: 50, set to 0 for unlimited).
Authentication
The admin interface is open by default. Configure authentication before deploying to production.
With Devise
ActiveCanvas.configure do |config|
config.authenticate_admin = :authenticate_user!
endWith a custom controller
ActiveCanvas.configure do |config|
config.admin_parent_controller = "Admin::ApplicationController"
endWith HTTP Basic Auth
ActiveCanvas.configure do |config|
config.authenticate_admin = :http_basic_auth
config.http_basic_user = "admin"
config.http_basic_password = Rails.application.credentials.active_canvas_password
endWith custom logic
ActiveCanvas.configure do |config|
config.authenticate_admin = -> {
unless current_user&.admin?
redirect_to main_app.root_path, alert: "Access denied"
end
}
endConfiguration
Full configuration reference:
# config/initializers/active_canvas.rb
ActiveCanvas.configure do |config|
# === Authentication ===
config.authenticate_admin = :authenticate_user! # method name, lambda, or :http_basic_auth
config.authenticate_public = nil # nil = public access
config.current_user_method = :current_user # for version tracking & AI features
# === CSS Framework ===
config.css_framework = :tailwind # :tailwind, :bootstrap5, or :none
# === Media Uploads ===
config.enable_uploads = true
config.max_upload_size = 10.megabytes
config.allow_svg_uploads = false
config.storage_service = nil # Active Storage service name
config.public_uploads = false # false = signed URLs
# === Editor ===
config.enable_ai_features = true
config.enable_code_editor = true
config.enable_asset_manager = true
config.autosave_interval = 60 # seconds (0 = disabled)
# === Pages ===
config.max_versions_per_page = 50 # 0 = unlimited
# === Security ===
config.sanitize_content = true
config.ai_rate_limit_per_minute = 30
endCustomization
Mount path
# config/routes.rb
mount ActiveCanvas::Engine => "/pages" # or "/cms", "/blog", "/"Override views
Copy any view into your app to customize it:
app/views/active_canvas/pages/show.html.erb
app/views/active_canvas/admin/pages/index.html.erb
app/views/layouts/active_canvas/admin/application.html.erb
Extend models
ActiveCanvas::Page.class_eval do
validates :content, presence: true
def excerpt
content.to_s.truncate(200)
end
endRake Tasks
bin/rails active_canvas:sync_models # Sync AI models from configured providers
bin/rails active_canvas:list_models # List all synced AI modelsDevelopment
git clone https://github.com/giovapanasiti/active_canvas.git
cd active_canvas
bundle install
bin/rails db:migrate
bin/rails testStart the dummy app:
bin/rails serverThen visit http://localhost:3000/canvas/admin.
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/my-feature) - Commit your changes
- Push to the branch (
git push origin feature/my-feature) - Create a Pull Request
License
The gem is available as open source under the terms of the MIT License.
