Observed since 20 Oct, 22:34 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Strapi plugin to easily sort your collections
A Strapi 5 plugin that enables hierarchical tree structures and flat sorting for your collections with drag-and-drop. Manage categories, menus, nested content, tags, and any data that benefits from ordered or hierarchical organization.
published or all)title, name, etc.) to display as the node labelreact-intl for internationalizationnpm install @0x30-ch/collection-tree
# or
yarn add @0x30-ch/collection-tree
# or
pnpm add @0x30-ch/collection-tree
In the Strapi Content-Type Builder, add the Tree custom field to your collection type. Configure:
title, name)published or all)For hierarchical (nested) trees, your content type must have self-referential relations:
parent pointing to itselfchildren pointing to itself (inverse of parent)The plugin uses the join table created by these relations to store sort order. Root node sort order is stored in the tree integer custom field itself.
If no parent/children relations are defined, the plugin works in flat mode — all entries are sorted at root level using only the tree column. The admin UI hides nesting controls (chevrons, child drop zones, expand/collapse all) automatically.
After configuration, restart your Strapi server to apply the schema changes.
Strapi 5 i18n creates separate database rows per locale, so each locale naturally has its own tree order. The plugin:
The plugin exposes three admin API endpoints (authenticated via admin::isAuthenticatedAdmin):
| Method | Path | Purpose |
|---|---|---|
GET | /collection-tree/content-types | List content types with tree fields (includes isLocalized and supportsNesting flags) |
GET | /collection-tree/nodes | Fetch tree nodes (uid, field, labelField, scope, locale params) |
PUT | /collection-tree/nodes | Save tree order (uid, field, nodes[], scope, locale body) |
Two public endpoints for retrieving sorted tree data in your frontend:
| Method | Path | Purpose |
|---|---|---|
GET | /api/collection-tree/flat | Flat list with depth and parentId fields |
GET | /api/collection-tree/nested | Nested structure with children arrays |
Query parameters (both endpoints):
| Param | Type | Description |
|---|---|---|
uid | string | (required) Content type UID (e.g. api::category.category) |
field | string | Tree field name (auto-detected if omitted) |
labelField | string | Label attribute to use |
scope | string | published (default) or all |
locale | string | Locale code (e.g. en, fr) — defaults to default locale for localized types |
fields | string | Comma-separated fields to include |
populate | string | Comma-separated relations to populate, or * |
Example:
# Flat list for French locale
GET /api/collection-tree/flat?uid=api::category.category&locale=fr
# Nested tree, published only
GET /api/collection-tree/nested?uid=api::category.category&scope=published&fields=title,slug&populate=image
git clone https://github.com/0x30-ch/strapi-plugins.git
cd strapi-plugins
pnpm install
# Start development
pnpm dev
# Build plugin
cd packages/collection-tree
npm run build
# Type checking
npm run test:ts:front # Frontend types
npm run test:ts:back # Backend types
# Verify plugin structure
npm run verify
packages/collection-tree/
├── admin/ # Frontend (React + Strapi Design System v2)
│ ├── src/
│ │ ├── components/
│ │ │ └── tree/ # Tree UI components
│ │ │ ├── styles.ts # Styled-components (connectors, cards)
│ │ │ ├── TreeNodeItem.tsx # Single node (chevron, handle, label, badge)
│ │ │ ├── TreeList.tsx # Recursive ReactSortable wrapper
│ │ │ ├── TreeSidebar.tsx # Left sidebar with content type list
│ │ │ └── TreeContent.tsx # Main area (header, expand/collapse, tree)
│ │ ├── hooks/
│ │ │ └── useTreeData.ts # Fetch/save logic + state management + locale
│ │ ├── pages/
│ │ │ └── TreePage.tsx # Thin orchestrator with locale dropdown
│ │ ├── types/
│ │ │ └── index.ts # Shared types (ContentTypeSummary, FlatNode, TreeNode, Locale)
│ │ ├── utils/
│ │ │ ├── getTranslation.ts # i18n helper
│ │ │ └── treeUtils.ts # Pure functions (buildTree, flattenTree, etc.)
│ │ └── translations/
│ │ └── en.json # English translations
│ └── tsconfig.json
├── server/ # Backend logic
│ ├── src/
│ │ ├── controllers/ # API controllers (tree.ts, tree-public.ts)
│ │ ├── services/ # Core service (resolveTreeModel, getNodes, updateNodes)
│ │ ├── routes/ # Route definitions (admin-api.ts, content-api.ts)
│ │ ├── utils/ # Tree helpers
│ │ └── config/ # Plugin configuration
│ └── tsconfig.json
└── package.json
The plugin uses self-referential relations for hierarchical trees:
manyToOne): Each entry can have one parentoneToMany): Each entry can have many children (inverse of parent)parent_id, child_id, and an order columntree custom field columnWhen a content type has a tree custom field but no self-referential parent/children relations:
tree custom field columnid)tree column (root ordering) is per-row, so per-locale by naturelocale parameter — when provided, queries filter by locale; when omitted, falls back to the default locale| Scope | Description |
|---|---|
published | Published entries only (default) |
all | All entries (drafts + published) |
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)MIT License - see LICENSE file for details.
Download counts and popularity will appear here as the collector receives them.
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 22:34 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:0940f1788eb680d261a7a939b31405f05214b4ce4040650f81718310c248e560