Tree menus
Menu manager and tree builder custom field
Strapi Menus
A plugin for Strapi CMS to customize the structure of menus and menu items.
Get Started
- Features
- Installation
- Configuration
- Extending
- User Guide
- API Usage
- Troubleshooting
- Support or Donate
- Roadmap
- Gallery
✨ Features
- Consumable menu data which can be used to render navigation and other menus in a frontend app.
- Easily manage menus with either a flat or nested structure.
- Customize the
title
,url
,isProtected
, and linktarget
of menu items. - Extend the schema and UI with custom attributes for menu items.
- Support for all Strapi field types in the UI.
- Support i18n translations for menu attributes.
- Support RBAC permissions for plugin.
💎 Installation
yarn add strapi-plugin-tree-menus@latest
Hot fix issue between vite and traverse
Unsupported fieldtype: plugin::tree-menus.tree
Vite 'global is not defined'
https://stackoverflow.com/questions/72114775/vite-global-is-not-defined
to fix enable global to browser
:
- create
vite.config.ts
in[root]/src/admin
1import { mergeConfig, type UserConfig } from 'vite'
2
3export default (config: UserConfig) => {
4 // Important: always return the modified config
5 return mergeConfig(config, {
6 resolve: {
7 alias: {
8 '@': '/src',
9 },
10 },
11 optimizeDeps: {
12 esbuildOptions: {
13 // Node.js global to browser globalThis
14 define: {
15 global: 'globalThis',
16 },
17 },
18 },
19 })
20}
Don't forget to restart or rebuild your Strapi app when installing a new plugin.
🔧 Configuration
property | type (default) | description |
---|---|---|
fieldSchema | object ({} ) | Schema for menu items. |
fieldSchema
The fieldSchema
prop is an object that defines the schema for menu items. This is where you can add custom attributes to the MenuItem
schema. The example below demonstrates how to add a custom example_field
attribute to the MenuItem
schema.
Example
1// ./config/plugins.js`
2'use strict'
3
4module.exports = {
5 'tree-menus': {
6 config: {
7 fieldSchema: {
8 attributes: [
9 {
10 id: 'title',
11 label: 'Title',
12 placeholder: 'Enter item title',
13 type: 'text',
14 validationType: 'string',
15 value: 'New items',
16 required: true,
17 validations: [
18 {
19 type: 'required',
20 params: ['this field is required'],
21 },
22 {
23 type: 'max',
24 params: [100, 'Title cannot be more than 100 characters'],
25 },
26 {
27 type: 'default',
28 params: ['New items'],
29 },
30 ],
31 },
32 {
33 id: 'url',
34 label: 'Url',
35 placeholder: 'Enter url',
36 type: 'text',
37 validationType: 'string',
38 value: '/',
39 required: true,
40 validations: [
41 {
42 type: 'required',
43 params: ['this field is required'],
44 },
45 {
46 type: 'max',
47 params: [200, 'Url cannot be more than 200 characters'],
48 },
49 {
50 type: 'default',
51 params: ['/'],
52 },
53 ],
54 },
55 {
56 id: 'target',
57 label: 'Target',
58 placeholder: 'Enter target',
59 type: 'select',
60 validationType: 'mixed',
61 value: '_self',
62 required: true,
63 validations: [
64 {
65 type: 'oneOf',
66 params: [
67 ['_blank', '_parent', '_self', '_top'],
68 'this field needs to be one of the following: _blank, _parent, _self, _top',
69 ],
70 },
71 {
72 type: 'default',
73 params: ['_self'],
74 },
75 ],
76 options: [
77 {
78 key: '_blank',
79 value: '_blank',
80 metadatas: {
81 intlLabel: {
82 id: 'tree-menus.target.options._blank',
83 defaultMessage: 'New window (_blank)',
84 },
85 disabled: false,
86 hidden: false,
87 },
88 },
89 {
90 key: '_parent',
91 value: '_parent',
92 metadatas: {
93 intlLabel: {
94 id: 'tree-menus.target.options._parent',
95 defaultMessage: 'Parent window (_parent)',
96 },
97 disabled: false,
98 hidden: false,
99 },
100 },
101 {
102 key: '_self',
103 value: '_self',
104 metadatas: {
105 intlLabel: {
106 id: 'tree-menus.target.options._self',
107 defaultMessage: 'Same window (_self)',
108 },
109 disabled: false,
110 hidden: false,
111 },
112 },
113 {
114 key: '_top',
115 value: '_top',
116 metadatas: {
117 intlLabel: {
118 id: 'tree-menus.target.options._top',
119 defaultMessage: 'Top window (_top)',
120 },
121 disabled: false,
122 hidden: false,
123 },
124 },
125 ],
126 },
127 {
128 id: 'isProtected',
129 label: 'isProtected',
130 placeholder: 'Choose isProtected',
131 type: 'bool',
132 validationType: 'boolean',
133 value: false,
134 required: true,
135 validations: [
136 {
137 type: 'required',
138 params: ['Need to choose isProtected'],
139 },
140 {
141 type: 'default',
142 params: [false],
143 },
144 ],
145 },
146 ],
147 },
148 },
149 },
150}
Enable menus in Documentation plugin
Default Strapi documentation included the tree-menus
documentation.
Supported field types
The following field types in the table below are supported. Some fields use a different type value for the schema and input type.
Field | Schema Type | Input Type |
---|---|---|
Boolean | boolean | bool |
Date | date , time , datetime | same |
email | same | |
Enumeration | enumeration | select |
Media | media | same |
Number | integer , biginteger , decimal , float | number |
Password | password | same |
Rich Text | richtext | wysiwyg |
Text | string , text | string , text , textarea |
The following field types are NOT supported:
- Component
- Dynamic Zone
- UID
- JSON
NOTE: By default, rich text fields are not supported unless a custom plugin overrides the core WYSIWYG editor, which is covered in the Strapi guide to creating a new WYSIWYG field in the admin panel.
📘 User Guide
Create
On the menus plugin home page, use the "Create new menu" button to get started. You will need to provide a title
and a unique slug
value for the new menu. Saving the menu before adding menu items is recommended but not required.
Clone
Choosing to clone an existing menu will take you to the edit view as usual, but this time it will be pre-populated with another menu's data. Once the cloned menu is saved, a brand new menu and menu items are created.
Delete
Deleting a menu will also delete all of it's menu items.
Edit
When clicking on a menu item in the left column, it will reveal action buttons to move the item, delete it, or give it a submenu.
The right column will reveal the edit UI for that item, where the title
is the only required field.
⚡ API Usage
Fetching menus data is the same as fetching any other data using Strapi's REST API features.
Don't forget to enable the public methods for
Menu
andMenuItem
in the Users and Permissions settings, likefind
andfindOne
.
Endpoints
request | endpoint | description |
---|---|---|
GET | /api/tree-menus/menu | Fetch all menus. |
GET | /api/tree-menus/menu/:id | Fetch one menu. |
POST | /api/tree-menus/menu/:id | Create a menu. |
PUT | /api/tree-menus/menu/:id | Update a menu. |
DELETE | /api/tree-menus/menu/:id | Delete a menu. |
POST | /api/tree-menus/menu/bulk-delete | Delete many menu. |
Basic example
Fetch a menu with the documentId
. Nothing is populated by default.
1await fetch('/api/tree-menus/menu/${documentId}')
Response
1{
2 "data": {
3 "id": 1,
4 "documentId": "jke4feqw23h1",
5 "title": "Main Menu",
6 "slug": "main-menu",
7 "items": [
8 {
9 "id": "1",
10 "title": "Home",
11 "url": "/",
12 "target": "_self",
13 "isProtected": false,
14 "children": []
15 },
16 {
17 "id": "2",
18 "title": "About",
19 "url": "/about",
20 "target": "_self",
21 "isProtected": false,
22 "children": [
23 {
24 "id": "2.1",
25 "title": "Our Team",
26 "url": "/about/our-team",
27 "target": "_self",
28 "isProtected": false,
29 "children": []
30 },
31 {
32 "id": "2.2",
33 "title": "Our Mission",
34 "url": "/about/our-mission",
35 "target": "_self",
36 "isProtected": false,
37 "children": []
38 }
39 ]
40 }
41 ],
42 "createdAt": "2024-10-07T08:00:00.000Z",
43 "updatedAt": "2024-10-07T08:00:00.000Z",
44 "publishedAt": "2024-10-07T08:00:00.000Z"
45 },
46 "meta": {}
47}
💩 Troubleshooting
In general
Remember to rebuild your app after making changes to some config or other code.
yarn build
# OR
yarn develop
Custom MenuItem
attributes save in the schema or config.
If you are having trouble saving custom attributes in the MenuItem
schema, make sure that the fieldSchema
object is properly configured in the config/plugins.js
file.
fieldSchema
only supports the following field types: string
, text
, number
, bool
, select
, date
, time
, datetime
, email
.
I can't see config fieldSchema
in the Strapi admin panel.
❤️ Support or Donate
If you are enjoying this plugin and feel extra appreciative, you can buy me a beer or 3 🍺🍺🍺.
🚧 Roadmap
📸 Gallery
Install now
npm install tree-menus
Create your own plugin
Check out the available plugin resources that will help you to develop your plugin or provider and get it listed on the marketplace.