A powerful M2A (Many-to-Any) interface for Directus with inline expandable editing that seamlessly integrates with Directus' native save system.
📚 Documentation • 🐛 Report Bug • ✨ Request Feature • 📦 NPM Package
- Documentation
- Why Expandable Blocks?
- Features
- Installation
- Usage
- Configuration Options
- Testing
- Development
- Contributing
- License
- Support
- Changelog
- Roadmap
For comprehensive documentation, visit our GitHub Wiki which includes:
- Detailed installation guide
- Configuration options
- Architecture overview
- API integration guide
- Development & debugging
- Security best practices
- Migration guide
- And much more!
Unlike other block editors, this extension works directly with Directus' native form system:
- ✅ No custom API calls - Uses Directus' built-in save/revert functionality
- ✅ Native Save & Stay - Works perfectly with Directus' save options
- ✅ Global Discard - Integrates with Directus' "Discard Changes" button
- ✅ Proper Dirty State - Save button only appears when changes exist
- ✅ No Data Loss - All changes tracked through Directus' form state
This means you get all the benefits of a sophisticated block editor while maintaining full compatibility with Directus' workflow!
- Inline Expandable Editing: Edit block content directly without opening separate forms
- Drag & Drop Sorting: Reorder blocks with intuitive drag-and-drop
- Native Integration: Uses Directus' save system - no custom API calls
- Smart Dirty State: Only sends changed blocks to the server
- Status Management: Quick status changes with visual indicators
- Visual Feedback: See which blocks have unsaved changes
- Compact & Full View Modes: Adaptive display options
- Keyboard Navigation: Full keyboard support
- Nested M2A Support: Handle complex nested relationships
- Custom Field Filtering: Show only specific fields in edit mode
- Template System: Pre-configured content templates
- Status Management: Built-in content status workflows
We're actively developing AI integration features that will include:
- AI Content Generation: Generate content using OpenAI GPT, Claude, or custom APIs
- Smart Content Improvement: Grammar, style, clarity, and SEO optimization
- Multi-Language Translation: Instant translation to multiple languages
- Context-Aware Suggestions: AI that understands your page structure
- Field-Specific Targeting: Generate content for specific fields only
💡 Early Access: AI features are currently in development on the
feature/ai-assistant
branch. Follow our GitHub repository for updates!
- Enable/disable sorting
- Show/hide item IDs
- Accordion mode (one block expanded at a time)
- Compact display mode
- Maximum block limits
- Custom field filtering
Install directly from the npm registry:
npm install directus-extension-expandable-blocks
The extension will be automatically loaded by Directus when you restart your instance.
- Download the latest release from GitHub Releases
- Extract to your Directus
extensions/interfaces/
directory - Restart Directus
For Docker setups, install via npm in your Dockerfile or mount the extension directory:
RUN npm install directus-extension-expandable-blocks
# or mount volume: -v ./extensions:/directus/extensions
- Navigate to Settings → Data Model → [Your Collection]
- Click "Create Field" button
- Choose "Many to Any Relationship (M2A)" field type
- Configure the relationship:
- Field Key: e.g.,
content_blocks
- Related Collections: Select which collections can be used as blocks
- Field Key: e.g.,
- In the field configuration, go to the "Interface" tab
- Click on the interface dropdown (default is "Many to Any")
- Select "Expandable Blocks" from the list
- The interface will change to show expandable blocks options
In the interface configuration panel, you can set:
-
Display Options
- ✅ Enable Sorting - Allow drag & drop reordering
- 📂 Start Expanded - Blocks open by default
- 🎯 Accordion Mode - Only one block open at a time
- 📱 Compact Mode - Condensed view for many blocks
-
Permissions
- 🗑️ Allow Delete - Users can remove blocks
- 📋 Allow Duplicate - Users can copy blocks
- 🔢 Max Blocks - Limit number of blocks (empty = unlimited)
- Click "Save" to apply the configuration
- Navigate to your collection items
- The M2A field will now use the Expandable Blocks interface!
# Main Collection (e.g., "pages")
Page Collection:
- id: primary key
- title: string
- slug: string
- content_blocks: M2A field → Uses "Expandable Blocks" interface
- status: string
- date_created: timestamp
# Junction Collection (auto-created by Directus)
pages_blocks:
- id: primary key
- pages_id: foreign key → pages.id
- collection: string (which block type)
- item: foreign key → block item id
- sort: integer (for ordering)
# Block Collections (your content types)
content_text:
- id: primary key
- headline: string
- subheadline: string
- content: text (rich text editor)
- alignment: string
content_hero:
- id: primary key
- headline: string
- subheadline: string
- button_text: string
- button_link: string
- image: file (image)
- overlay: boolean
content_gallery:
- id: primary key
- title: string
- images: O2M → gallery_images
- columns: integer
- spacing: string
📸 Click to see setup screenshots
-
Creating M2A Field
- Select M2A relationship type
- Configure related collections
-
Selecting Interface
- Change from default "Many to Any"
- Select "Expandable Blocks"
-
Interface in Action
- Inline editing capability
- Drag & drop sorting
- Visual status indicators
The extension integrates seamlessly with Directus' form system:
// Traditional approach (what we DON'T do):
async function saveBlock(block) {
await api.post('/items/content_blocks', block) // ❌ Custom API call
await refreshData() // ❌ Manual sync
updateUI() // ❌ Manual UI update
}
// Our approach (native integration):
function handleBlockChange(blocks) {
emit('input', blocks) // ✅ Let Directus handle everything
}
Only modified blocks are sent to the server:
// Example: 3 blocks, only middle one edited
[
"block-1-id", // ✅ Unchanged - send ID only
{ id: "block-2-id", title: "New" }, // ✅ Changed - send full object
"block-3-id" // ✅ Unchanged - send ID only
]
This means:
- 🚀 Faster saves - Less data transmitted
- 🛡️ Conflict prevention - Unchanged blocks aren't touched
- 📊 Better performance - Server processes only what changed
Option | Type | Default | Description |
---|---|---|---|
enableSorting |
boolean | true |
Allow drag & drop reordering |
showItemId |
boolean | true |
Display item IDs in headers |
startExpanded |
boolean | false |
Start with all blocks expanded |
accordionMode |
boolean | false |
Only one block expanded at a time |
compactMode |
boolean | false |
Use compact display |
maxBlocks |
number | null |
Maximum number of blocks |
isAllowedDelete |
boolean | true |
Allow block deletion |
isAllowedDuplicate |
boolean | true |
Allow block duplication |
The extension includes comprehensive test coverage:
# Unit tests
npm run test
# E2E tests
npm run test:e2e
# Test coverage
npm run test:coverage
# Watch mode
npm run dev
# Install dependencies
npm install
# Development build with watching
npm run dev
# Production build
npm run build
# Link for local development
npm run link
For detailed information about the data flow, state management, and debugging techniques, see our comprehensive Architecture Documentation. This includes:
- Complete data flow lifecycle with visual diagrams
- Detailed state management explanations
- Store interactions and timing
- Debugging techniques and helpers
- Common issues and solutions
- Performance optimization tips
We welcome contributions! Please see our Contributing Guide for details on:
- Development setup
- Testing procedures
- Pull request process
- Code standards
- Issue templates
MIT License - see LICENSE file for details
Report issues on GitHub Issues
See CHANGELOG.md for detailed version history.
Check out our Development Roadmap to see what's coming next:
- 🤖 AI-Powered Content Generation (v1.1.0)
- 🎨 Enhanced UI/UX Features
- 🔧 Developer Tools & CLI
- 🚀 Performance Optimizations
For comprehensive documentation, visit our GitHub Wiki:
- 🏠 Getting Started - Overview and quick start
- 📦 Installation Guide - Detailed setup instructions
- ⚙️ Configuration - All configuration options
- 🏗️ Architecture - Technical deep dive
- 💾 Data Flow - State management explained
- 🔌 API Integration - Working with Directus APIs
- 🛠️ Development - Developer guide
- 💡 Examples - Practical use cases
- 🤝 Contributing - How to contribute
- 🗺️ Roadmap - Future plans
- 🔄 Changelog - Version history
Made with ❤️ for the Directus community