What is Directus?

Open-Source Headless CMS & Content Delivery API

Directus Core is a free and open-source self-hosted platform published under the GNU (v3) license. It provides a simple and intuitive web interface for managing database content with completely custom architectures.

What is a Headless CMS?

Traditional CMS are built specifically to quickly deploy and manage websites – typically simple sites and blogs. However, today’s content is consumed by a much wider range of applications, including complex web platforms, native apps, wearables, kiosks, IoT devices, and other data-driven projects.

Content should be created and managed independently from the place it will be used. By decoupling and authoring application-agnostic content, you gain the freedom to use it anywhere. Moreover, you can use it everywhere, since you are no longer limited to a single platform.

Database Mirroring

Directus differs from other CMS in that it directly mirrors your database – essentially making it a safe and client-friendly database GUI. Instead of your content being stored in a proprietary blackbox datastore, Directus gives you complete control over optimizing the actual database architecture based on your specific project needs. Whether managing an existing or new database, Directus and its API will always stay up to date with schema changes.

Your Data

Directus only manages your database, not your project, so it doesn't stand between you and your content. All system info is stored in separate tables, so your data always remains pure and unmuddled. There are three ways to access your data:

What is Directus Hosted?

Directus Hosted is a highly scalable DBaaS (Database as a Service) platform built with, and extending, the Directus Core codebase. It allows you to quickly create and manage multiple instances of Directus, each with it's own asset storage and custom content delivery API endpoints. We handle the servers, storage, and setup – so you can focus on your project.

When to Use Directus


Directus is an excellent alternative to traditional CMS. Small sites have less bloat to deal with and worry about. Large sites get total control over database architecture for optimizing performance. Complex sites are more attainable since you're no longer limited to the technologies, libraries, and languages of an all-in-one solution. And because everything's decoupled you'll be prepared for the future should you need to support additional clients such as a native app.

Native Apps

Directus is a CMS that wasn't built exclusively for websites – so there's no more struggling to find a good way to manage content or even raw data from your Native App(s). But that doesn't mean you're stuck with a highly technical datastore interface, you and your clients can interact with data through intuitive and safe interfaces.

Multi-Client Projects

Perhaps you know in advance that your project will require a website, native app, and kiosk – Directus has you covered. With agnostic data you can connect any and all facets of your project to a single, centralized source of content. Use our API or SDKs to connect each client in the appropriate language.

Experimental Projects

Maybe you don't even know what you're building! From experiential installations to IoT devices, sometimes you just need a quick way to visualize or manage data. Sequel-Pro, PHP-My-Admin, and the command line aren't always the easiest (or safest) way manage files, validation, and relational data – why make things harder than they need to be?

Stand-Alone Tool

Finally, not all data needs to be connected. Does your company need Project Management, CRM, or other tools? Why pay for a service that you may outgrow or be limited by? Why work with disparate platforms for each service when you can combine them into one system? With Directus you can easily design your own tool with its own workflows and interfaces – no limitations.

User Guide


If you attempt to access Directus without being authenticated, you will be directed to the login page. Once successfully logged in, if you were initially following a link to a certain page you will be redirected there – otherwise you'll be taken to the last page you visited within Directus.

Version and Commit Hash: The Directus version is displayed at the bottom of the login screen. Hovering over this will reveal the exact git commit hash of your version.

Forgot Password

Directus salts and securely encrypts your password, so not even admins can see your actual password in plain-text. Therefore no one can tell you what your password is – if forgotten, it must be reset. To reset your password, navigate to the login page, fill in your email address, and click the "?" icon within the password input. You will then receive an email with a link to reset your password that expires after a short period of time.

Logging Out

It's a good habit to log out of Directus when not actively using it – especially on public computers/devices. The logout button is located in the user menu, accessible when hovering over your avatar/name in the bottom-left corner of the screen. Administrators of your system may choose to set an Auto Logout Duration for idle users – by default this is set to 1 hour.

Security Note: Leaving your computer awake and unattended while your Directus user is logged-in could result in a security breach.


These interfaces are global and can be seen on all pages of the platform.


The left sidebar is an easy way to access any tables you have permission to view as well as any bookmarks you have created. On smaller screens this sidebar may appear hidden and is then accessible by clicking on the "hamburger" icon in the header.

User Menu

In the bottom-left corner of the platform is your avatar and name – hovering over this will open the user menu. The following are accessible from this menu:


In the platform's header there is a set of links that show which page you are on, and any parent pages. Clicking on any of these breadcrumbs will take you to that page. On smaller screens this collapses down to only show the current page.


Bookmarks are an easy way for users to save specific pages/views for quick access. Clicking the bookmark icon directly to the right of the breadcrumb prompts the user for a custom name, and saves the current page into the navigation. Bookmarks can be deleted by hovering over them and clicking the "X" icon that appears.

The following parameters are saved when bookmarking:

Item Listing

This page lists all items within the selected table. It provides several views for filtering, sorting, reordering, bulk-editing, deleting, and updating item status. All page parameters are persistent and per user – so users should feel free to customize each table to their liking. Click on an item to view/edit it – or the "+" button in the header to create a new item.


The “Filter” input allows you to quickly search for things. For more advanced column filters click the triangle on the right of the input to expand a dropdown. Each column filter you add is considered an “AND” (not "OR") – so if two filters are added, items are shown that match both. Clicking the "×" on the right-side will remove that filter.

Changing Item Status

By selecting the checkbox to the left of an item you are given the option to delete it. Deleting items is permanent and should be done with care. If the table has Status enabled then items are "soft-deleted" – meaning they are permanently removed but can be retrieved by an Administrator. Tables with Status enabled may also have additional options for items such as "Draft".

Default Status Options:

Developer Note: When enabling Status on a table, remember to only fetch active items (active=1) in your project's data queries.


Clicking column headers will sort the data by that field. Clicking a column again will reverse the sort direction – clicking a third time will remove sorting reverting to the default order: date created. This is automatically saved on a per user basis and is persistent until changed.


While sorting simply changes the current view of the items, reordering actually saves the order within each item. For tables that have Reordering enabled, items will automatically show a handle on the left side of their row for drag-and-drop interaction. To reorder items (or see the saved order) you must click on the "double arrows" icon at the top of that far-left column.

Developer Note: While reordering is an excellent way to curate the order of items, for tables with large datasets it becomes unwieldy and should not be used.

Bulk Editing

When selecting the checkboxes for multiple items an option to “Batch Edit” will appear. Clicking this button will take you to a blank edit page where bulk edits can be made. When bulk editing, you must enable each field you wish to edit – all enabled fields will save that value for all selected items.

Caution: Use this feature with care as it is easy to overwrite data for a large number of items.

Customizing the View

By default the item listing page shows a tabular view with the first few columns visible. However users can adjust how to visualize items and which columns they see by clicking the "three dots" icon on the right-side of the header. Core view options include:


The default view – this option is always available.


An alternate view, available when there is at least one FILE interface within the table.


An alternate view, available when there is at least one DATE/TIME interface within the table.


An alternate view, available when there is at least one LOCATION interface within the table.

Item Detail

This page displays all the fields the current user has permission to view. The Create, Edit, and Bulk Edit pages are all essentially the same, except for the content within the fields. Fields may have a note/comment to further explain their purpose, or an asterisk (*) beside their label if required.


Once you've added or edited content, clicking the Save () button in the header will save the item and return you to the Item Listing page. For additional save options you can click the "three dots" icon on the top of this button:

Item Comments

You can leave comments on items within the right sidebar. You can mention specific users or groups with the "@" character. When mentioned in comments, users will receive a Directus notification and an accompanying email (if enabled within their preferences).

Revisions & Rollbacks

Every change made by users within Directus is tracked to comprehensive accountability. You can view the following details of each revision in the right-sidebar: The user, their IP address and user-agent, and the date/time of the change. Any changed data (delta) and a complete item backup are all saved for all revisions – you can Rollback to any previously saved state.



File Library

This page provides a listing of all files uploaded into the Directus platform as well as any videos or embeds (eg: YouTube, Vimeo) that have been linked. Full-featured and intuitive, this page performs the role of asset management.

Developer Note: You can add additional columns to directus_files to extend and customize this system.


"Nestable" folders are displayed at the top of the page.

Developer Note: Directus folders are not mirrored as directories on the filesystem.

Uploading New Files

If your user has uploading enabled, you can upload files to the library in several different ways:

The following default fields are available for each file:

Swapping Files

If you have added a file to many items throughout the system and need to change it globally, you can swap the asset. This maintains all relationships and metadata – but swaps the file itself.


Directus saves a single thumbnail (200x200) for system purposes. Administrators can adjust the quality (default: 100%) of these thumbnails within Settings. Thumbnails are also pulled from Embed videos when possible.

User Directory

This page provides a listing of all Directus users on the platform. Clicking on a user's avatar or name anywhere within the platform will open a modal window with their public info (eg: name, email, and location). Other information is private and can only be seen by that user and Administrators (eg: token, encrypted password). No one has the ability to see your actual password, not even Administrators.

Inviting New Users: Only Administrators can add/invite new users.

Developer Note: You can add additional columns to directus_files to extend and customize this system.


Directus includes a simple messaging system where you can send messages to individual users or entire groups and have the option to include attachments. This page also aggregates all your @mentions in item comments creating a single place to see all project communication. There is also a option to have your Directus messages forward on to your email address.

Who's read my message? To improve accountability, each message includes a listing of users who have seen the post.

Sending New Messages

  1. Click the "+" button in the Messages header to open a compose window
  2. Enter a Subject for the message
  3. Choose who should receive the message
    • You can choose any other users or groups
  4. Enter your message in the Body field
  5. Add any file attachments (Optional)
  6. Click the "Send" button in the compose header
Administrator Guide

Simple Installation


Directus is a forward-looking framework and therefore may not support certain legacy systems. If your server is not compatible with the requirements below, please contact your host to upgrade.

Optional Enhancement: Installing Imagick adds thumbnail support for TIFF, PSD, and PDF files

Server Preparation

  1. Check that your server meets the requirements above
  2. Download and unzip the latest Directus package version from here
  3. Create a database and MySQL user with access/modify privileges on your server.
  4. Upload the files to a public directory on your server
  5. Run the installation script by accessing the URL where you uploaded the files

Existing Databases: You can also use an existing database, but should look at the typical Directus Schema

Installation Wizard

A pre-installation check will run but will only be shown if the server requirements are not met.

Step 1 – Project Info

Step 2 – Database

Other Database Types: SQLite, PostgreSQL, and MongoDB support are in development

Step 3 – Confirmation

Security Note: Once you have completed the install, make sure to delete install folder.


If you're having problems with your Directus install, please visit our troubleshooting section.

Versions & Updating

Note: Only Administrators have access to these pages and settings.

Below is the process for manually updating Directus. Directus Hosted Instances are automatically updated.

  1. Backup your database: In addition to scheduled backups, you should always create another complete database backup directly before making any broad CMS/database changes
  2. Download and unzip the newest Directus package from here
  3. Ensuring you don't replace your /api/config.php and /api/configuration.php files, overwrite your previous version of Directus with the new files
  4. Run the migration script to update your database/schema. Read how to

Note: Developers who installed Directus by cloning the git repository directly to their server can simply pull the newest stable version. Typically this is as simple as navigating to the Directus folder and running: git pull origin build

Global Settings

Note: Only Administrators have access to these pages and settings.

Once Directus is installed you can customize it for your specific project.

Files Settings: Changing File/Thumbnail settings will not affect any files already uploaded into Directus.

Storage Adapters

Beyond local storage, Directus comes with the ability to connect to a number of popular file storage systems such as CDNs, Amazon S3, Rackspace, Azure and Dropbox. Directus uses Flysystem, so you can easily implement other custom adapters.

Learn more about creating new custom adapters on Flysystem's site.

Storage Adapters configure different destinations for writing/reading files within Directus. Currently they are defined at the instance-level within the api/configuration.php file's filesystem attribute.

Core Storage Adapters

Other Supported Storage Adapters

Other Possible Storage Adapters

Other Adapters: If your desired adapter is not listed above you can search for other Flysystem options or create your own custom adapter.

Adapter Parameters

'filesystem' => [
    // adapter name
    'adapter' => 'local',
    // By default media directory are located at the same level of directus root
    // To make them a level up outsite the root directory
    // use this instead
    // Ex: 'root' => realpath(BASE_PATH.'/../storage/uploads'),
    // Note: BASE_PATH constant doesn't end with trailing slash
    'root' => BASE_PATH . '/storage/uploads',
    // This is the url where all the media will be pointing to
    // here all assets will be (yourdomain)/storage/uploads
    // same with thumbnails (yourdomain)/storage/uploads/thumbs
    'root_url' => '/storage/uploads',
    'root_thumb_url' => '/storage/uploads/thumbs',
    //   'key'    => 's3-key',
    //   'secret' => 's3-key',
    //   'region' => 's3-region',
    //   'version' => 's3-version',
    //   'bucket' => 's3-bucket'

Installing Adapters

Each adapter's GitHub page explains how to install, but generally you use composer such as in the following example command for the Amazon S3 V3 adapter:

composer require league/flysystem-aws-s3-v3

Configuring S3

'filesystem' => [
    // adapter name
    'adapter' => 's3',
    // this path within the S3 Bucket
    // where the files are going to be uploaded to
    'root' => '/storage/uploads',
    // This is the url where all the media will be pointing to
    // here all assets will be (yourdomain)/storage/uploads
    // same with thumbnails (yourdomain)/storage/uploads/thumbs
    'root_url' => '<your-s3-url>/storage/uploads',
    'root_thumb_url' => '<your-s3-url>/storage/uploads/thumbs',
    'key'    => '<your-s3-key>',
    'secret' => '<your-s3-secret-key>',
    'region' => '<your-s3-region>',
    'version' => '<your-s3-version>',
    'bucket' => '<your-s3-bucket-name>'

Switching Storage adapters

In order to switch from adapter to another, follow the next steps:

All files should be pointing to the S3 server now.

Code Samples

When extending the functionality of Directus Core or your project, it may be necessary to invoke the storage adapter component. Listed below are some references to code samples which demonstrate various ways of deploying storage adapters.

These links point directly to the commit hash which is current as of this writing, so that line numbers do not move out of sync with the documentation. Remember to compare these code snippets with the current state of Directus code in the future, and to update if necessary.


The definition of the upload route shows the most generic (core) way of implementing the storage adapter interface. We instantiate a new \Directus\Media\Storage\Storage object and then call the acceptFile method with the incoming local file. The interface automatically fetches the default/thumbnail adapters and passes this information to them.


The authenticated media proxy front controller uses a slightly more complex implementation. After identifying the Directus Media record being requested, it fetches the record for the corresponding storage adapter. It then passes this record to the method \Directus\Media\Storage\Storage::getStorage, which returns the corresponding adapter object, allowing the front controller to fetch the file contents abstractly, irrespective of the underlying driver or file location.


This implementation uses the same approach as the authenticated media front controller, however it performs more complex operations, such as checking if a file exists, and writing a file to the adapter.

Users, Groups, & Permissions

Note: Only Administrators have access to these pages and settings.

There is no limit to how many users or groups you can add within Directus. Your first user (created during installation) is always part of the system's Administrator Group. Subsequent users can either be assigned to the Administrator Group (unrestricted access to tables and settings) or to any other custom group with specific permissions.

Creating New Groups

  1. Navigate to the Settings > Group & Permissions
  2. Click the (+) button at the top of the page
  3. Setup the group options below:

Group Options


Group have granular permissions for each table saved into directus_privileges, below are the possible settings:


Access Value Description
All 3 The user can view all table items
Group 2 The user can only view table items created by users within their group
User 1 The user can only view table items they created themselves
None 0 The user can not see any of this table's items


Access Value Description
All 1 The user can add new items to this table
None 0 The user can not add new items to this table


Access Value Description
All 3 The user can edit all table items
Group 2 The user can only edit table items created by users within their group
User 1 The user can only edit table items they created themselves
None 0 The user can not edit any of this table's items


Access Value Description
All 3 The user can delete all table items
Group 2 The user can only delete table items created by users within their group
User 1 The user can only delete table items they created themselves
None 0 The user can not delete any of this table's items

Note: User/Group options require the table having a directus_tables.user_create_column set.

Column Read Blacklist

Click this area to select any fields that this group can not view.

Column Write Blacklist

Click this area to select any fields that this group can not edit.

Status (Workflows)

All of the above permissions apply across the board. If the table has a status column, you can also click the "Workflow" icon to set different permissions for each status state.

Workflow Example: Intern Group

  1. Interns can create new Blog items (default to status: draft)
  2. Interns can edit all Blog drafts
  3. Interns can view all live Blog items, but can not edit them

Show in Sidebar

The nav_listed toggle allow you to hide certain tables from the Directus sidebar and table listing. In certain cases you may want records to be visible throughout the CMS (eg relational modals) but you don't need that table listed prominently otherwise.

Navigation Override

You can also add a JSON directus_groups.nav_override to achieve similar results, but for simply hiding a few tables that might be overkill.

Example of navigation override: @TODO

Deleting User Groups


Creating New Users

  1. Navigate to the Users page from the sidebar
  2. Click the (+) button at the top of the page
  3. Fill in the applicable fields. Required fields include:
    • First Name
    • Last Name
    • Email
    • Password
    • Group
  4. Click the Save Button

Tables & Columns

Existing Databases

One key benefit to Directus is that it can manage existing databases with custom architectures. This page outlines the few requirements and guidelines for new or existing schemas to work properly within Directus. We're working to remove these, allowing for complete flexibility.

Table & Column Naming

Directus uses your table and field names for presentation to the users, so it's important to ensure they make sense when formatted and read by users. Below is the automatic formatting performed to convert schema names to display names:

Examples of proper formatting:

Examples of improper formatting:

Creating New Tables


See Table & Column Naming above

Display Template



See Creating New Columns below

Listing Views


Hide Table

This toggle will hide the table throughout the system.

Single Item

While not a traditional way to architect a database, sometimes you may find yourself with a table intended to contain only a single item/record. Perhaps you want to manage the content for your project's "About" page with the fields: title, hero_image, and company_description. Any table marked as "single" will skip the Item Listing page and take users directly to the Edit Item page.

Note: The single managed item should have an ID of 1

Primary Key

Currently Directus requires that every table contains a unique, auto-incremented, primary key named id.

Primary Key Example:

`id` int(11) unsigned NOT NULL AUTO_INCREMENT

The abstracting/decoupling of this column name is under development.

Sort Column

Adding a sort(INT11) column to a table turns on Directus' drag-and-drop sorting. Items on the Directus listing page will now have handles for dragging them into curated orders. When sorted, Directus will save ascending integers into the order field, thereby making it easy to return results in this order:

Developer Note: It is important when fetching data for your project to honor the sorting of items as needed. This can be accomplished by adding ORDER BY sort ASC to SQL queries.

Developer Note: Dragging items within long lists can be difficult – it is not advisable to use the manual sort feature on tables with many items.

Status Column

Adding an active(INT11) (default name) column enables "soft" deleting and "status" states for items within the table. A status could be used in many different ways, the default is: Published, Draft, or Deleted but you can customize this as needed in the /api/configuration.php file.

Developer Note: It is important when fetching data for your project to honor the status of items. Typically this means only fetching published content. Assuming you are using the default Status options, that would mean limiting all SQL queries with: active = '1'

@TODO: Link to configuration

Status Mapping



While Directus uses the directus_activity table to store users/dates for creation/modification – you can also have it save these details into the actual table.

Below is an example SQL query for generating a simple table that considers these guidelines:

CREATE TABLE `articles` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `active` tinyint(1) unsigned DEFAULT '2',
  `sort` int(11) DEFAULT NULL,
  `title` varchar(100) DEFAULT NULL,
  `article` text,
  `publish_date` datetime DEFAULT NULL,
  `author` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)

Deleting Tables


Creating New Columns

Name (Database)

@TODO (Strict Naming Toggle)

See Table & Column Naming above

Name (Display)


See Table & Column Naming above


You can add helpful notes to your interfaces (shown just below the label) to add clarity or further instructions. Notes default to the columns comment in the database, but those can be overridden within Settings > Tables & Inputs.

User Interface



Directus uses each column's datatype to determine which Interfaces are allowed. The most common Core interface is chosen as a default, but you can change this within Settings > Tables & Inputs. Below are a few examples of how datatypes influence the interface:




Directus uses your database's column defaults for interfaces. For example, if a TINYINT has a default value of 1 the checkbox interface will be checked by default. When used in conjunction with the Status column you can choose a default state for items (eg: Draft).



Required Columns

If selected, this field name will display an asterisk and be required to save the item.

Hidden Columns

This toggle will hide the column throughout the system. For more control, use Column Permissions.

Reordering Columns

By default Directus will display interfaces in the same order as the database columns, however this can be overridden within Directus Settings > Tables & Inputs. The reason we don't alter the column order within the database itself when changing input order is because some database optimization involves the specific ordering of the fields.

Deleting Columns



By design, Directus keeps things simple. Only key features utilized by the majority of users make it into the core framework. Anything not handled by the Core platform can be added via extensions. Extensions take advantage of the system’s authentication, ACL, and data connections – they are a blank canvas of extensibility with no restrictions.

Examples of Extensions:

Creating an Extension


Interface Guide

Core Interfaces

User Interfaces (UIs) are how your users will interact with the different types of content in your database. Each field in your database has a data-type that determines what kind of content can be stored there – Directus uses this information to determine a default UI and any other UIs that are supported. In addition to the provided Core UIs, you can also easy create new Custom UIs for specific or complex use-cases.

Text Input

Supported Datatypes: VARCHAR, DATE, TIME, ENUM

A simple, basic, single-line text field for almost any kind of string data. The user is shown the remaining character count based on the length property of the column. Despite being so simple, a few key options make this UI one of the most useful and flexible:

Text Area

Supported Datatypes: TEXT, VARCHAR

A multi-line text field for longer plain-text data. New lines are saved in the database as new lines as this input does not create any HTML tags.


Supported Datatypes: VARCHAR, TEXT

A multi-line text field for longer HTML content with WYSIWYG formatting buttons. Each formatting button can be toggled on or off as needed.

Slug, or Short Name

Supported Datatypes: VARCHAR

Often clean/semantic URLs will include a contextual identifier based on an item's title rather than the id alone. This UI automatically creates that URL-friendly value based on another input. For instance, if linked to the title column, as the user types "This, That & the Other!" the slug input would live-update to "this-that-the-other".

Password & Salt

Supported Datatypes: VARCHAR

This UI contains a masked input for entering a password, a second input to confirm the password, a "Generate New" button for optionally generating new secure passwords quickly, and a "Reveal Password" toggle to expose the password to the user in plain-text. This UI can also be linked to a "salt" column so that the password entered is securely hashed.



TODO: Combine with text-field. A simple input that only accepts number characters. Alternatively you could use the text-input UI and set the appropriate validation.

Hex Color

Supported Datatypes: VARCHAR

Accepts only 6 hexadecimal characters and provides a live-colored square matching the value. This UI also uses the HTML5 color-chooser for easy color selection. Allowed hexadecimal characters include: 0123456789ABCDEF

Datetime (Date, Time, and Year)

Supported Datatypes: DATETIME, DATE, TIME, YEAR This UI provides a set of drop-downs and numerical inputs for entering months days, years, hours, minutes and seconds based on the datatype. HTML5 helpers are available for easier date/time selection. All values are saved in the ISO 8601 standard: yyyy-mm-dd hh-mm-ss

Select (Drop-down or Radio Buttons)

Supported Datatypes: VARCHAR, INT

Sometimes you don't need to create a dedicated table to house relational dropdown options but you still want to limit the values a user can choose. This UI will save a single non-relational value chosen from a set of JSON options.

TODO: Merge in Radio Buttons

Multi-Select (List or Checkboxes)

Supported Datatypes: VARCHAR, TEXT

Sometimes you don't need to create a dedicated table to house relational dropdown options but you still want to limit the values a user can choose. This UI will save multiple non-relational values from a set of JSON options. All selections are glued together with a delimiting character.


Supported Datatypes: TEXT, VARCHAR, CHAR

A system for entering and saving comma-delimited tags – for relational tags use the many-to-many UI instead. Tags are entered into the list upon hitting the comma or enter key and are deleted by clicking the tag in the list. The tags are saved into the database as a CSV string with commas at the beginning and end (",ranger,studio,range,") which lets you perform powerful LIKE filters. For example, by using commas you can filter with %,range,% to get results that are exact (does not include "ranger") or %range% for inclusive (does include "ranger").


Supported Datatypes: TINYINT

Checkbox is the default UI for the TINYINT datatype – saving either a 1 (checked) or 0 (unchecked). It also honors the default value of the database, which is the proper way to set the UI's initial state.


Supported Datatypes: INT

An HTML5 adjustable slider for choosing a number.


Supported Datatypes: VARCHAR, TEXT

TODO: Add ALIAS datatype support and remove VARCHAR and TEXT. No need to have an actual column for this.

This is an easy way to include verbose or formatted instructions or other helper information within the Item Detail page. If the column Note isn't enough room to adequately describe the column's guidelines or you need table/level instructions, this is a good way to add context.


Supported Datatypes: VARCHAR, ALIAS

This UI provides an visual and interactive way to select a specific location, which is then Geocoded and dynamically fills in any linked address fields.

Relational Interfaces

Since SQL is a relational database, Directus has several UIs that can help clearly and easily manage relationships between items. An item can have related items from the same table (eg: Project -> Related Project) or a different table (eg: Project -> Category). An item can also have relationships to a single item (eg: Shirt -> Size) in a Many-to-One (or One-to-Many) relationship, or to multiple items (eg Shirt -> Materials) through Many-to-Many relationships.

Single File (M2O)

Supported Datatypes: INT

This is a M2O relational UI that links a single file (and YouTube/Vimeo video embeds) by storing the related item's ID – which is why this UI only works with the INT datatype (also used for IDs). If you have a books table you could use this for the cover_image column, for a projects table this would be the UI used for columns like: thumbnail_image, wireframe_pdf, and background_video. Basically any column that needs to store a single file.

Multiple Files (M2M)

Supported Datatypes: MANYTOMANY (an alias datatype)

This is a M2M relational UI that links multiple files (and YouTube/Vimeo video embeds). If you have an album table you could use this to store mp3s within tracks, for a projects table this would be the UI used for columns like: hero_slideshow, youtube_playlist, and press_pdfs. Basically any column that needs to store multiple files. Currently only Directus Files are accepted by this UI, so directus_columns.table_related must always be set to directus_files.

Many-to-Many (M2M)

Supported Datatypes: MANYTOMANY (an alias datatype)

This is a M2M relational UI that will make it easy to link the item being edited to multiple other items from another table (or the same table). You can use this to relate multiple tags to projects, or multiple staff to their respective office. But be careful about creating a schema/architecture with recursive or deeply nested M2Ms – otherwise you'll end up with a confusing "Matryoshka doll" user experience.

Many-to-One (M2O)

Supported Datatypes: INT

This is a M2O relational UI drop-down that links to a single item by storing the related item's ID in the column – which is why this UI only works with the INT datatype (also used for IDs). You could use this to relate each item in your press table to a project, or to relate

Many-to-One Type-Ahead

Supported Datatypes: INT

Similar to the Many-to-One in function, the interface for this UI is a type-ahead (live search auto-complete) which is useful for large relational datasets that won't fit into a dropdown.

One-to-Many (O2M)

Supported Datatypes: ONETOMANY (an alias datatype)

Similar to Many-to-One, this UI is an interface for the opposite direction. Instead of saving the id of a related item in an actual column, the One-to-Many is an Alias column that saves a FK on the related items.

When deleting these related O2M items, the following rules apply to the related table:

Translation (O2M)

Supported Datatypes: ONETOMANY (an alias datatype)

This relational UI allows for field translations to be stored in a related table. For example, if you have a projects table you could create a projects_translations table to relationally store the name and description columns in other languages. Some data, such as hero_image or featured may not need to be translated and can be added directly to the projects table.

Warning: There's a known issue where the translation UI will stop working if you add UIs/Relationships to the junction table columns.

Example Schema

UI Options

Relational Options

When creating the translation column within Directus you'll need to enter some info about the relationship. These are some helpful reminders:

And here is the SQL to create the above example:

# Create the table that stores supported languages
CREATE TABLE `languages` (
  `language` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)

# Add the supported languages
INSERT INTO `languages` (`id`, `language`)

# Add a `projects` table with language-agnostic columns
CREATE TABLE `projects` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `active` tinyint(1) DEFAULT '2',
  `sort` int(11) DEFAULT NULL,
  `title` varchar(100) DEFAULT NULL,
  `client` varchar(100) DEFAULT NULL,
  `tags` text,
  `date` date DEFAULT NULL,
  PRIMARY KEY (`id`)

# Add a table for the translated columns
CREATE TABLE `projects_translations` (
  `name` varchar(100) DEFAULT NULL,
  `description` text,
  `language_id` int(11) DEFAULT NULL,
  `project_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)

# Add the relationship data for the translation column
INSERT INTO `directus_columns` (`table_name`, `column_name`, `data_type`, `ui`, `relationship_type`, `related_table`, `junction_key_right`)
  ('projects', 'translations', 'ONETOMANY', 'translation', 'ONETOMANY', 'projects_translations', 'project_id');

# Add the UI options for the translation column
INSERT INTO `directus_ui` (`table_name`, `column_name`, `ui_name`, `name`, `value`)
  ('projects', 'translations', 'translation', 'id', 'translation'),
  ('projects', 'translations', 'translation', 'languages_table', 'languages'),
  ('projects', 'translations', 'translation', 'languages_name_column', 'language'),
  ('projects', 'translations', 'translation', 'default_language_id', '1'),
  ('projects', 'translations', 'translation', 'left_column_name', 'language_id');

Custom Interfaces

UI Structure

For most content types the Directus Core User-Interfaces should work well. However if you're looking for a specific interface or have proprietary or custom content types to manage – you may need or want to create something more tailored. Directus UIs are modular Javascript files (or dedicated sub-directories for complex interfaces) that allow you to create new ways of interfacing with database content.

Text inputs, checkboxes, and WYSIWYG editors are pretty standard – but perhaps your application needs a JSON editor, unique hashtag generator, iTunes song lookup, or custom seating-chart. So let's look at how these would be created within Directus, starting with the components of the UI.


Each UI must have a unique identifier. For instance the Single File ID is single_file.


Each UI works with a specific set of datatypes, for instance a Text Area UI might work with TEXT or VARCHAR datatypes, while a Single File UI only works with INT (storing the related file ID).


Options are the key to making the UI usable in different circumstances. Options are easy to create, and actually use OTHER UIs as the interface for setting the value (so meta). Below is an example of an dropdown option to determine the size of an interface:

{id: 'size', ui: 'select', required: true, options: {options: {'large':'Large','medium':'Medium','small':'Small'} }, comment: "Choose a size!"},


Whether it's simple enough to add (escaped) in the single Javascript file, or to be included from a separate template file – this is where you define your markup and any additional styling. Directus uses the Handlebars templating library – so be sure to reference their Docs for any questions you may have.


In addition to how the content is manipulated on the Edit Item page, you also need to consider how the data will be presented on the Item Listing page (more tabular). Each UI has a separate list function to handle that view.


Of course you're going to want to ensure that data is saved correctly into the column – so you should always include logic in the validate function.

Creating Your First UI

The easiest way to get started is to take one of the existing Core UIs and duplicate it. If possible, try to find one with similar functionality that supports the same datatype(s). Here are the steps for using an existing UI as a starter template to create your new UI:

  1. Copy a Core UI file from /app/core/uis into the custom UI directory: /customs/uis, giving it a new name.
  2. On define Change core/UIView to core/CustomUIView.
  3. Set a unique name for the UI:
  4. Choose which SQL datatypes this UI will support: Module.dataTypes
  5. Optionally, add UI Options that admin users can adjust per column: Module.variables
  6. Draft up some markup with inline styling within the template variable.
    • You will also see examples of escaped CSS in the template variable, but remember to namespace any styling.
  7. Add any validation into the Module.validate function which is called when attempting to save the model. If there is an error message the system automatically shows it as an alert. There are two parameters available within this function:
    • value: String : Submitted value for this column's UI
    • options: Object : Contains the options from Module.variables for this UI
      • collection [TableCollection]
      • model [EntriesModel]
      • schema
      • settings
  8. Finally, format the value for display on the Item Listing page within the Module.list function. There is one parameter available:
    • options: Object : Contains the value and options for this UI
      • value
      • collection [TableCollection]
      • model [EntriesModel]
      • schema
      • settings

At this point, all you have to do is refresh your instance of Directus and your new UI will be available. If you go to Settings > Tables & Inputs > []Table] and open the User Interface dropdown for the desired column, if that column's datatype is supported you will see your UI as an option. If you don't see it, confirm that that datatype is listed in Module.dataTypes and that the is unique – otherwise check the error logs.

Example UI

As an example we are going to create a Text Area with a limited character-count, and will update the remaining characters as the user add or remove characters from the interface.

UIs depends on two objects, UIComponent and CustomUIView (optionally).


define(['core/UIComponent', 'core/CustomUIView'], function(UIComponent, UIView) {
    var charactersLimit = 100;
    var Input = UIView.extend({
        templateSource: '<textarea maxLength="{{maxLength}}"></textarea><span id="count">{{charactersRemaining}}</span>',
        events: {
            'keyup textarea': 'updateCount'
        updateCount: function(event) {
            var textLength = event.currentTarget.value.length;
            var textRemaining = charactersLimit - textLength;
        serialize: function() {
            return {
                maxLength: charactersLimit,
                charactersRemaining: charactersLimit

Template Source

Template source can be any valid Handlebars source, which include plain HTML and CSS. For larger or more complex UIs you may wish to include a separate template file instead of escaping the markup in Javascript.


We can bind events to any element inside the UIView but we cannot bind to elements outside the view. If for some reason you want to bind to a element outside, you can use the global jquery object $.

Listening to keyup events on the text area will execute updateCount method.

Update Count

The uppdateCount method will execute every time the user enter characters in the text area and updates the counter.


The serialize method returns the data that is going used when templateSource is rendered.


var Component = UIComponent.extend({
    id: 'textlimit',
    dataTypes: ['TEXT'],
    Input: Input


id is the UI's unique name.

Data Types

dataTypes are the supported data types for the UI. In this case we want to support only TEXT column type. The first value here is the default.


This is the input view for the component.

Complete UI code

define(['core/UIComponent', 'core/CustomUIView'], function(UIComponent, UIView) {
    var charactersLimit = 100;
    var Input = UIView.extend({
        templateSource: '<textarea maxLength="{{maxLength}}"></textarea><span id="count">{{charactersRemaining}}</span>',
        events: {
            'keyup textarea': 'updateCount'
        updateCount: function(event) {
            var textLength = event.currentTarget.value.length;
            var textRemaining = charactersLimit - textLength;
        serialize: function() {
            return {
                maxLength: charactersLimit,
                charactersRemaining: charactersLimit

    var Component = UIComponent.extend({
        id: 'textlimit',
        dataTypes: ['TEXT'],
        Input: Input

    return Component;

Enabling an UI

To use a UI simply copy it into your /customs/uis directory.

In Directus 6.4, there is a minor change, all UIs must be inside a directory, and the entry point file must be named component.js.

Copy the file component.js into /customs/interfaces/myinterface instead.

UI Options

In the previous section we explained how to create a UI that limits how many characters they can type into a text area while showing the characters remaining.

In the example, we directly create a variable with a 100 character limit – but what if this limit is changed? What if you want to use different limit on a different field? The solution to this is UI options.

UI options are settings for each interface found within the Setting panel. These values are not shared between UIs, but are specific to each column.

Adding options

Add a new property inside Component object named variables.

variables property is an array of objects that will each represent a setting inside the column settings editor.

Options structure

The structure of each option is the following:

    id: 'option-id',
    ui: 'textinput',
    default_value: 'default_value',
    comment: 'This is a comment for this ui setting'

Now let's add a new option for our Text Area characters limitation UI:

var Component = UIComponent.extend({
        id: 'textlimit',
        dataTypes: ['TEXT'],
        variables: [
            {id: 'characters_limit', ui: 'numeric', default_value: 100, comment: 'Characters Limitation'}
        Input: Input


define(['core/UIComponent', 'core/CustomUIView'], function(UIComponent, UIView) {
    var Input = UIView.extend({
        templateSource: '<textarea maxLength="{{maxLength}}"></textarea><span id="count">{{charactersRemaining}}</span>',
        events: {
            'keyup textarea': 'updateCount'
        updateCount: function(event) {
            var charactersLimit = this.options.settings.get('characters_limit');
            var textLength = event.currentTarget.value.length;
            var textRemaining = charactersLimit - textLength;
        serialize: function() {
            var charactersLimit = this.options.settings.get('characters_limit');
            return {
                maxLength: charactersLimit,
                charactersRemaining: charactersLimit

    var Component = UIComponent.extend({
        id: 'textlimit',
        dataTypes: ['TEXT'],
        variables: [
            {id: 'characters_limit', ui: 'numeric', default_value: 100, comment: 'Characters Limitation'}
        Input: Input

    return Component;

System Interfaces

These are Core UIs that are used primarily by the Directus framework for internal purposes.

Other Interfaces

Developer Guide


Composer Dependency Management

The Directus API is available in PHP (Node is in development) and employs a number of third-party, open-source packages to manage routing, rendering, database access, and so on.

Best practice says that the composer.lock file should be tracked in the application repository. This helps to ensure stability by guaranteeing that over time, and across the development team, the versions of application dependencies will not change with each installation, but will remain exactly the same.

This is especially critical from the perspective of Directus code integration and inter-dependency integration. Case in point: the Slim package (version 2.4) introduced breaking changes in its integration with Twig. Therefore changes to the composer.lock file are extremely sensitive. Here are some guidelines on how to modify Directus’ composer dependencies.

Never run composer.phar update

This “updates” the version of each dependency to the latest version. If for whatever reason you need to re-install your vendors, always instead remove your vendor directory and re-run composer.phar install

Avoid modifying the composer.json file directly

Always modify dependencies using composer.phar because it will modify the lock file directly. For example, to add a new dependency, run this command:

composer.phar [require]( vendor/package:version

This command updates your JSON file and your lock file without changing the versions of your other dependencies.

Requirements (Development Specific)

Note: Directus may work with other HTTP Server solutions such as Lighttpd and Caddy, but it will need proper configurations and tests.

Step 1: Setup a Development Environment

You will need to setup a local *AMP (Apache, MySQL and PHP) or *EMP (nginx, MySQL and PHP) environment in order to test Directus. *AMP packages can be found for different platforms:

Step 2: Install git

You will need Git to keep your test version of Directus up to date. Unless you already have Git installed, you can download it from here.

Step 3: Pull Directus from GitHub

Create a directory named "directus" in the www root. Next, open the directory in the terminal and run:

$ git clone directus

Step 4: Install Dependencies

Directus uses composer to handle its php dependencies. Go to the directus/ folder and install composer:

$ curl -s | php

Then install the dependencies by executing:

$ php composer.phar install

Read more at the official site on how to download and install composer, or how to install composer globally.

Step 5: Setup the Database

The *AMP-packages listed above all include PHP-My-Admin. The following three steps need to be completed in order to setup the database

Note: Please disable MySQL Strict Mode before installation. Directus does not fully support Strict Mode due to limitations with the PDO and MySQL Drivers.

Using PHP-My-Admin

  1. Create the database
    1. Databases -> Create Database
    2. Choose a database name and select utf8_general_ci as Collation
  2. Create a user
    1. Once you've created the database it will be visible in the left column – click it
    2. Privileges -> Add user
    3. Fill in a username and password. Leave the other fields as defaults
  3. Import the Directus core database schema
    1. Import->File to Import->Choose file
    2. Open api/schema.sql
    3. Press Go

Using Command Line

  1. Connect to MySQL Server

    $ mysql -u <mysql-user-name> -p
    # mysql will ask to type the server password.
    $ Enter password: ****

Change with the database username, typically is root by default.

  1. Create the Database After successfully connect to the MySQL Server, create a database by typing:

    mysql> CREATE DATABASE <database-name>`;

    Change with the desired name for the database – then you can exit the server by typing exit.

  2. Install Directus

    1. Go to http://your-directus-host.local/installation and follow the steps and skip Step 6
    2. Or Import Directus core database schema and do Step 6

      $ mysql -u <mysql-user-name> -p <database-name> < api/schema.sql
      # mysql will ask to type the server password.
      $ Enter password: ****

Step 6: Setup Directus

Open api/config_sample.php and add the database, username, and password from Step 5 to DB_NAME, DB_USER and DB_PASSWORD. Save the file as directus/api/config.php

define('DB_USER',       'myusername');
define('DB_PASSWORD',   'mypassword');
define('DB_NAME',       'mydatabase');

Step 7: Setup Files Uploads (Optional)

Directus supports different storage adapters, by default all files are uploaded to a directory on the server filesystem using the local adapter.

Under api/configuration.php there's a filesystem array key-value where you configure your storage. Read more

Step 8: Done!

Open Directus by navigating your browser to the path where it was installed in your local host.

If Directus was installed manually, log in using the default user and password password

Configuration Files

These files are automatically generated during the installation process, alternatively you can utilize the sample files to setup manually.


This file contains project-specific constants for the Directus framework. These values are typically not changed after initial setup.


This file contains the configuration arrays for the Directus framework.

Supported transport:

    'mail' => array(
        'from' => array(
            '' => 'John Doe'
        'transport' => 'mail'
    'mail' => array(
        'from' => array(
            '' => 'John Doe'
        'transport' => 'sendmail'
    'mail' => array(
        'from' => array(
            '' => 'John Doe'
        'transport' => 'smtp',
        'host' => '',
        'username' => 'user',
        'password' => 'pass',
        'port' => '123',

Default Status Mapping

0 => [
    'name' => 'Deleted',
    'text_color' => '#FFFFFF',
    'background_color' => '#F44336',
    'subdued_in_listing' => true,
    'show_listing_badge' => true,
    'hidden_globally' => true,
    'hard_delete' => false,
    'published' => false,
    'sort' => 3
1 => [
    'name' => 'Published',
    'text_color' => '#FFFFFF',
    'background_color' => '#3498DB',
    'subdued_in_listing' => false,
    'show_listing_badge' => false,
    'hidden_globally' => false,
    'hard_delete' => false,
    'published' => true,
    'sort' => 1
2 => [
    'name' => 'Draft',
    'text_color' => '#999999',
    'background_color' => '#EEEEEE',
    'subdued_in_listing' => true,
    'show_listing_badge' => true,
    'hidden_globally' => false,
    'hard_delete' => false,
    'published' => false,
    'sort' => 2

IP Whitelisting for User-Groups

If your project requires that certain user-groups have access limited to specific IP addresses you can set their directus_groups->restrict_to_ip_whitelist to 1. Then enter any allowed IP addresses (and a brief description) into the directus_ip_whitelist table.

Sidebar Navigation Blacklist for User-Groups

By default, Directus shows all tables that the current user's group has list and view access to. To hide certain tables from this list on a group basis, simply enter a CSV of table names into directus_tab_privileges.directus_tab_blacklist.

Customizing the Sidebar Navigation for User-Groups

By default, Directus displays all available tables alphabetically in the navigation sidebar under the "Tables" group header. To customize this you can build a tailored JSON string and save it within directus_tab_privileges->nav_override for the desired user group.

    "Office": {
        "Staff": {
            "path": "/tables/staff"
        "Locations": {
            "path": "/tables/locations"
    "Portfolio": {
        "Projects/Work": {
            "path": "/tables/projects"
        "Clients (Brands)": {
            "path": "/tables/clients"

Custom Data Workflow

Tables containing a status column (default column name of active) track the publish state of their items/records. Initially, options of Live(1), Draft(2), and Deleted(0) are all options for all tables with a status column. These options, and their associated display-color and saved-value, are editable and extendable within the configuration.php file.

Furthermore, all table permissions for user-groups can be assigned globally or for specific status states. The following should outline this extensive level of customization:

To create a custom data workflow for your users simply set different permissions for each individual status state. This will allow you to restrict which status options are available to users, when they're editing an item of a given state.

Example Schema: Project Management

├── projects
│   ├── id
│   ├── active
│   ├── title
│   ├── description
│   ├── url
│   ├── client
│   ├── date_started
│   ├── project_manager
│   └── sow_pdf
├── clients
│   ├── id
│   ├── logo_image
│   ├── name
│   ├── hourly_rate
│   ├── point_of_contact_email
│   ├── point_of_contact_name
│   └── point_of_contact_phone
├── tasks
│   ├── id
│   ├── active
│   ├── sort
│   ├── title
│   ├── category
│   ├── assigned_user
│   ├── created_by
│   ├── date_created
│   └── description
├── task_categories
│   ├── id
│   └── title
└── project_tasks (junction table)
    ├── id
    ├── project_id
    └── task_id


Hooks allow project-specific code to interact directly with Directus Core events. The hooks are very simple to implement: just add custom logic to the (untracked) config file at /directus/api/configuration.php.

All the logic you need to implement that will perform an action, such as send an email to users when a post is created, it should be defined under hooks in the configuration file.

All the logic you need to implement that will modify the data being used, such as change the post published date time based on timezone, it should be defined under filters in the configuration file.

Creating an Action Hook

This hook will be executed every time a record is inserted in any table.

Add the event name table.insert to the configuration (api/configuration.php) hooks key, if the key does not exists, add it.

Using a function

    'hooks' => [
        'table.insert' => function ($table, $data) {
            // execute any code

You can also you any class that implement the __invoke method instead of a function.

Using a class implementing __invoke


namespace \App\Events;

class InsertEvent
    public function __invoke($table, $data)
        // execute any code
    'hooks' => [
        'table.insert' => '\App\Events\InsertEvent'

Using a class implementing HookInterface


namespace \App\Events;

class InsertEvent implements \Directus\Hook\HookInterface
    public function handle($table, $data)
        // execute any code
    'hooks' => [
        'table.insert' => '\App\Events\InsertEvent'

Creating an Filter Hook

If you need to add a custom data before being inserted into a table, you need filters hook.

Add the filter name table.insert.[table-name]:before to the configuration (api/configuration.php) filters key, if the filters key does not exists, please add it.

The filter can be add it the same way as the action hook, anonymous function, classes implementing __invoke or implementing HookInterface interface.

    'filters' => [
        'table.insert.users:before' => function (\Directus\Hook\Payload $payload) {
            $payload->set('secret_key', rand());

            return $payload;

The filters passes a Payload object as paramter, which contain the data and attribute information related to the filter hook, such as the table name the record belong to.

Payload Object

All filters hook now passes a Payload object as parameter instead of an array to represent the data being filtered, using the Payload object make easier to pass the data over multiple filters. Each filter function must return the Payload so other filters can interact with the updated data.

Useful methods

Name Description
getData() Get the payload data
attribute($key) Get an attribute key. Ex $payload->attribute('tableName')
get($key) Get an element by its key
set($key, $value) Set or update new value into the given key
has($key) Check whether or not the payload data has the given key set
remove($key) Remove an element with the given key
isEmpty() Check whether the payload data is empty
replace($newDataArray) Replace the payload data with a new data array
clear() Remove all data from the payload

NOTE: get() and has() method can use dot-notation to access child elements. ex get('').

Payload object is Arrayable which means you can interact with the data as an array $payload['data']['email], but you can't do \Directus\Util\ArrayUtils::get($payload, '').

IMPORTANT: All these filters are triggered to Directus core tables as well, so make sure if you don't want to interact with directus tables omit them by checking for them first, as right now all tables are prefixed with directus_ in front of them.

$table = $payload->attribute('tableName');
if (\Directus\Util\StringUtils::startsWith($table, 'directus_')) {
    return $payload;

Action/Event Hooks list

Name Description
application.boot When the API start booting. Before all endpoints are being set. app object is passed.
application.init API has booted but the endpoint hasn't been dispatched. app object is passed.
application.shutdown API has ran and it's about to shutdown. app object is passed.
application.error An Error has occurred. Exception object is passed.
directus.authenticated User has been authenticated. app object and user info (array) is passed.
directus.authenticated.token User has been authenticated through the API. app object and user info (array) is passed.
directus.authenticated.admin User has been authenticated through the Web App. app object and user info (array) is passed.
directus.start User has been authenticated and API is about to start. app object is passed.
table.create:before Before a table is being created. Table name passed.
table.create:after After a table has been created. Table name passed.
table.create Alias for table.create:after
table.drop:before Before a table is being dropped. Table name is passed.
table.drop:after After a table has been dropped. Table name is passed.
table.drop Alias for table.drop:after
table.insert:before Before new record is being added to a table. Table name and record data passed.
table.insert:after After new record has been added to a table. Table name and record data inserted passed.
table.insert Alias for table.insert:after
table.insert.[table-name]:before Before new record is being added to [table-name]. Record data passed.
table.insert.[table-name]:after After new record has been added to [table-name]. Record data inserted passed.
table.insert.[table-name] Alias for table.insert.[table-name]:after
table.update:before Before a table record is being updated. Table name and record data passed.
table.update:after After a table record has been updated. Table name and record data passed.
table.update Alias for table.update:after
table.update.[table-name]:before Before a [table-name] record is being updated. Record data passed.
table.update.[table-name]:after After a [table-name] record has been updated. Record data passed.
table.update.[table-name] Alias for table.update.[table-name]:after
table.delete:before Before a table record is being deleted. Table name passed.
table.delete:after After a table record has been deleted. Table name.
table.delete Alias for table.delete:after
table.delete.[table-name]:before Before a [table-name] record is being deleted.
table.delete.[table-name]:after After a [table-name] record has been deleted.
table.delete.[table-name] Alias for table.delete.[table-name]:after
files.saving Before a file is being save. Filename and size is passed.
files.saving:after After a file has been saved. Filename and size is passed.
files.thumbnail.saving Before a file thumbnail is being saved. Filename and size is passed.
files.thumbnail.saving:after After a file thumbnail has been saved. Filename and size is passed.
files.deleting Before a file is being deleted. File info is passed.
files.deleting:after After a file has been deleted. File info is passed.
files.thumbnail.deleting Before a file thumbnail is being deleted. File info is passed.
files.thumbnail.deleting:after After a file thumbnail has been deleted. File info is passed.
directus.index.start Index page starts.
directus.login.start Login page starts.

Filter Hooks list

Name Description
response Before adding the content into the HTTP Response body
response.[method] Same as response but it only executes on a specific method, such as GET, POST, DELETE, PATCH, PUT, OPTIONS
response.[table-name] Same as response but it only executes on a specific table
response.[table-name].[method] Same as response.[method] but it only executes on a specific table
table.insert:before Before inserting a record into any table
table.insert.[table-name]:before Before inserting a record into [table-name] table After a table query was executed in any table
table.[table-name].select After a table query was executed in the specific table

Directus CLI

Introduction and Help

Directus Command-Line Interface (CLI) provides commands that allow you to perform various tasks such as installation, resetting a user's email, or upgrading the database to the most recent Directus schema.

You can use the help command at any time to learn about available CLI actions:

# this will provide information about the current modules
php bin/directus help

To get more information on an specific command you can type "help" followed by the command:

# this provide information about the **install** module
php bin/directus help install

Install Module

Includes commands to install and configure Directus.

Configure Directus:

Creates the config.php and configuration.php files.

IMPORTANT: This command will overwrite any existing config.php and configuration.php files.

php bin/directus install:config -h <db_host> -n <db_name> -u <db_user> -p <db_pass> -r <directus_root> -d <directus_path> -e <directus_email>

Example: http://example.local

php bin/directus install:config -h localhost -n directus -u root -p pass

Example: http://example.local/directus

php bin/directus install:config -h localhost -n directus -u root -p pass -d directus

Populate the Database Schema:

Creates all of the Directus Core tables based on the configuration files: /api/config.php and /api/configuration.php.

php bin/directus install:database

Install Initial Configurations:

Create the default admin user and the site's default settings.

php bin/directus install:install -e <admin_email> -p <admin_password> -t <site_name>


php bin/directus install:install -e admin@directus.local -p password -t "Directus Example"

User Module

Includes commands to manage Directus users

Change User Password:

php bin/directus user:password -e <user_email> -p <new_password>


php bin/directus user:password -e admin@directus.local -p newpassword

Database Module

Includes commands to manage Directus database schema

Note: This require that Directus has a valid connection configured in api/config.php. Read more

IMPORTANT: Always backup your database before run the database module to prevent data loss.

Install Directus schema:

php bin/directus db:install

Upgrade Directus schema:

php bin/directus db:upgrade

Language Module

Includes commands to manage Directus languages files

See all the missing locale keys compared to English:

php bin/directus language:diff

Vagrant Box Install



Download and install VirtualBox

Download and install Vagrant

Clone our Vagrant configuration:

git clone

Or download the zip file

$ cd [vagrant dir]
$ vagrant up

What's on the server?


The server now can be access from:

NGINX Configuration

At this point it is assumed that you already have installed and configured nginx and have a basic understanding of nginx.

Directus NGINX Configuration

Important: This is an incomplete guide, but it should help you get Directus installed and set up on nginx. It was tested on: Ubuntu 14.04.1 - nginx/1.4.6 (Ubuntu)

Site configuration

Since nginx doesn't use .htaccess, which does the url rewriting, we therefore need to do the rewrite within nginx itself. Below we configure an nginx server block.

Open the default server block file:

sudo vim /etc/nginx/sites-available/default

The nginx server block should look like this:

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /path/to/directus;
    index index.php index.html index.htm;

    server_name localhost;

    location / {
        try_files $uri $uri/ =404;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

IMPORTANT: Some NGINX packages are distributed without SCRIPT_FILENAME param inside their fastcgi_params to avoid problems please add fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; before include fastcgi_params;.

Change location / to this:

location / {
    try_files $uri $uri/ /index.php$args;

Add a new location:

location /api {
    if (!-e $request_filename) {
        rewrite ^/1/extensions/([^/]+) /api/api.php?run_extension=$1 last;
    rewrite ^ /api/api.php?run_api_router=1 last;

Add another location to prevent uploaded php/html files to execute.

location ~ ^/(media|storage)/.*\.(php|phps|php5|htm|shtml|xhtml|cgi.+)?$ {
    add_header Content-Type text/plain;


To prevent direct access to extensions api.php file we need to edit /etc/nginx/sites-available/default and add a new location:

location ~* ^/customs/extensions/api\.php$ {
    return 403;

Custom endpoints

Prevent direct access to api endpoint files.

location ~* /customs/endpoints/ {
    deny all;

Fonts Mime types

To serve fonts mime types correctly we need to edit /etc/nginx/mime.types and add these lines in it:

application/    eot;
font/truetype                    ttf;
font/opentype                    otf;
font/woff                        woff;

PHP Values

The result of these two files (/etc/nginx/mime.types, /etc/nginx/sites-available/default) and can be found in here config/nginx


Schema Guide

This page provides insight into the core tables containing all system information, allowing your project data to remain decoupled and unsullied by Directus. Therefore at any point these core tables could be deleted and only your pure database content would remain.

Custom Tables & Fields

The fundamental purpose of Directus is to allow developers to design and create schemas specifically around the needs of their project's scale, performance, architecture, and extensibility. While Directus tries to make no assumptions about your data, there are a few standards which (as of now) should be followed when creating new tables and columns:


This table stores all changes made to item's through Directus. It stores a complete item snapshot, the changes, editor, and other information used in the item history.

Column Value Description
type The scope of the activity, such as: LOGIN, ENTRY, UI, FILES, or MESSAGE
action The action performed within the scope type, such as: ADD, UPDATE, LOGIN, etc
identifier A Human Readable name saved for the activity. This is not relational and won't be updated if the original item is changed later
table_name The table name where the activity took place
row_id The item's record id where the activity took place
user The ID of the Directus User that performed the activity
data The full data saved during the activity. This may not include relational data affected during this activity
delta The difference between the original and new data
parent_id The ID of the parent item when a change is made relationally
parent_table The name of the table when a change is made relationally
parent_changed [0,1] A boolean for if the parent item was also changed
datetime The date and time that the activity took place
logged_ip The IP address of the user that performed the activity
user_agent The browser/client user agent that performed the activity


This table stores all of the left-nav bookmarks for Directus. This includes bookmarks that users create as well as the "System" bookmarks at the bottom. Each record is assigned to a specific user.

Column Value Description
user [Directus user id] This assigns the bookmark to a specific user (there's a ticket to allow for "global" bookmarks using NULL)
title The text to display in the navigation menu
url The path to navigate to when clicked, relative to the Directus root
icon_class Deprecated
section ["search" or "other"] Which nav section to show the link within. User generated bookmarks use "search", while all system links go within "other"


This tables stores all the info about columns managed by Directus. Namely UI, relationships, and other metadata.

Column Value Description
table_name The name of the table containing the column being edited
column_name The name of the column being edited
data_type The datatype of the column. Other database types (eg SQLite) have limited datatypes and this more granular value is used to know exactly how to format/type API responses. If you change the datatype directly in the database be sure to update this value. Some Directus fields are not actual columns (such as ONETOMANY), these are saved as an ALIAS and represent the ghost column
ui This stores the current User-Interface ID
relationship_type MANYTOONE, MANYTOMANY, ONETOMANY This column stores the relationship type (NULL if non-relational). As an ENUM there are three options:
related_table Only for relational columns, this value holds the table containing the related data
junction_table Only for MANYTOMANY relational columns, this value holds the junction/bridge/associative table name. This is the table that stores two foreign keys which link related items between two tables
junction_key_left Only for MANYTOMANY relational columns, this value holds the column name (in the junction table) that stores "this" item's ID
junction_key_right Only for relational columns, this value stores the column name that stores the "right" key: For MANYTOMANY, the column name (in the junction table) that stores the related item's ID. For MANYTOONE, the column name (in the this table) that stores the related item's ID. Should be the same value as column_name. For ONETOMANY, the column name (in the related table) that stores this item's ID
hidden_input [0,1] Whether or not this edit page field will be hidden from all users. This is global and overrides any user group permissions
hidden_list [0,1] Whether or not this listing page value will be hidden from all users. This is global and overrides any user group permissions
required [0,1] Whether or not the field is required before saving the edit item page
sort [1,2,3...] This stores the sort order for the Directus fields. This is based on the database column order but can be changed since column order is tied to lookup/optimizations and the CMS view shouldn't impact that. New items are stored with a 9999 until they are sorted with the drag-and-drop Settings interface
comment String This stores a note to be displayed beside the field on the edit page. This is based on the database column comment but has been decoupled since some database types (eg SQLite) don't natively support comments


This table stores the metadata for all files uploaded through Directus. Because files are an integral part of the framework and its interfaces, Directus does not currently support using other tables to manage files. However, you can create your own "File" tables as a "wrapper" by using the Single/Multiple File UIs.

Column Value Description
active Whether a file is active (1) or soft-deleted (0)
name The file-system name of the asset as it is saved within the storage adapter
title The display name of the file
location A column to store the location metadata for the file. This is pulled from the IPTC data for images but can be overridden
caption A column to store the caption metadata for the file. This is pulled from the IPTC data for images but can be overridden
type The MIME type for the file. eg image/png
charset Typically binary
tags A column to store the CSV keywords metadata for the file. This is pulled from the IPTC data for images but can be overridden
width The width in pixels for image files
height The height in pixels for image files
size The size in bytes for files, or the duration in seconds for video embeds (pulled from embed API when possible)
embed_id The embed ID (YouTube or Vimeo) for externally stored embedded assets
user The ID of the Directus User who uploaded/added the file
date_uploaded The date and time the file was uploaded/added
storage_adapter Which storage adapter this file is saved within. eg local or s3


This table stores the User Groups for the Directus privileges system.

Column Value Description
name The name of the user group. "Administrator" is the first/default group and should always be ID = 1
description Stores a description to help remember the group's purpose
restrict_to_ip_whitelist Ignored when NULL, a CSV of IP addresses can be entered to limit this group's access
nav_override @TODO
show_activity [0,1] Whether or not the Activity nav item is visible/available to this group
show_messages [0,1] Whether or not the Messages nav item is visible/available to this group
show_users [0,1] Whether or not the Users nav item is visible/available to this group
show_files [0,1] Whether or not the Files nav item is visible/available to this group
nav_blacklist A CSV of nav item titles that should not be displayed to this group


This table stores all user/group messages and item comments used by the framework.

Column Value Description
from Directus user ID of who sent the message
subject The subject of the message
message The body of the message. This allows for @user commenting
datetime The date and time the message was sent
attachment The Directus file ID for an attached file @TODO
response_to If this message is a threaded response to another, the parent's ID will be stored here
comment_metadata @TODO


This table tracks all the recipients of each message to know if the message was "read" or not.

Column Value Description
message_id The ID of the message
recipient The ID of the individual recipient
read [0,1] A boolean for if the message has been viewed/read.
group Soon to be deprecated. @TODO


This table stores all of the individual user preferences which allows a user's experience to remain the same between sessions/devices.

Column Value Description
user The user's ID
table_name The table that the preferences will be saved for
title A user-generated title for the preferences (used for Bookmarks only)
columns_visible An ordered CSV of the column names that this user has visible on the table's Item Listing page
sort The column name to sort by
sort_order The sort direction for the sort column. Available options: ASC, DESC
status CSV A CSV of the status IDs (active column by default) to be shown. Eg: 1,2 would show active and draft, but not deleted (0)
search_string A string saving any enabled search filters. Eg: location%3Alike%3ABrooklyn%2Ctitle%3Alike%3A2016


Each row on this table is associated with a table (table_name) and a Directus user group (group_id). Optionally, you can increase the fidelity of your privileges by setting the status_id to a an allowed status integer. For instance, you can define a user-group's view privileges for a table specifically when the status of a record is in draft mode. This might be useful if you'd like your "intern" user-group to only see draft content (not live/published).

The read_field_blacklist functions by omitting the given column from the database schema which the API yields, such that the front-end app has no awareness of this column. The write_field_blacklist prevents that user group from writing to this column.

The nav_listed boolean is used to hide certain tables from the Directus sidebar and table listing. In certain cases you may want records to be visible throughout the CMS (relationally or otherwise) but you don't need that table listed prominently otherwise. You can also use directus_groups.nav_override to achieve similar results, but for simply hiding a few tables that might be overkill.

The allow_[permission] columns determine which operations the group may perform on this table. Possible values are: 0 (not allowed), 1 (allowed for content you created), 2 (allowed for content anyone created). To differentiate between who created content (and take advantage of these granular privileges), you must set directus_tables.user_create_column to the field within that table which will track the Directus User-ID of the creator.

Column Value Description
allow_view [0,1,2] The ability to view a table. Without this permission, the table will be completely omitted from the schema of users in this group
allow_add [0,1] The ability to add new items to this table. A value of 2 is not an option since you can't create someone else's content
allow_edit [0,1,2] The ability to edit items from this table
allow_delete [0,1,2] The ability to delete items from this table
allow_alter [0,1] The ability to modify the table's schema


This system table tracks your current Directus version and the database schema migrations you have run to ensure your database architecture is up to date.

Column Value Description
version year+month+day+hour+min+sec for when the version file was created so Directus knows the order to run migrations.


This table stores global Directus settings for this instance in name-value pairs.

Column Value Description
collection Soon to be deprecated, this is the category for the name-value-pair
name The unique name or key for the parameter
value The value assigned to the parameter


This is where system information is stored about all tables managed by Directus.

Column Value Description
table_name The table name being configured, this must be unique.
hidden [0,1] Determines if the table is completely hidden from Directus (1) or not (0).
single [0,1] Determines if the table contains only one record/item (1) or multiple (0). When Single tables are clicked in the sidebar, the Item Listing page is skipped, taking users directly to the Item Edit page. The lone item should have an id of 1.
default_status [1...] This is the table's default status value – which must be an option within the configuration file's Status Mapping.
footer [0,1] Determines if a table footer should be shown on the Item Listing page with helper functions for INT columns such as: Average, Min, Max, etc.
list_view Allows for the Item Listing page to be overridden with a custom view template. @TODO
column_groupings Soon to be deprecated, this column was used to group columns on the Item Edit page.
primary_column This stores the column name for the table's unique primary key. As of now Directus requires all tables to use an id column here.
user_create_column Optional. Enter the name of a column to store the Directus User ID that created the item.
user_update_column Optional. Enter the name of a column to store the datetime that the item was created.
date_create_column Optional. Enter the name of a column to store the Directus User ID that last modified the item.
date_update_column Optional. Enter the name of a column to store the datetime that the item was last modified.
filter_column_blacklist A CSV of column names in this table that should not be included in the Item Listing page's filter component.


This tables stores all of the global options/settings for the column User Interfaces (UIs). Since you can change UIs, you may have values within this table that are no longer used – however this allows switching between UIs without losing the saved options therein.

Column Value Description
table_name The table that contains the column
column_name The column that uses the UI
ui_name The UI being edited
name The name of the UI option being saved
value The value of the UI option being saved


All the users, including admins, are added within this table. Each user is assigned to a single User Group (directus_groups), and that group determines the privileges (directus_privileges) for all of its users.

Column Value Description
active [0,1] Determines if the user is active (1) or not (0)
first_name The first name of the user
last_name The last name of the user
email The (unique) email address for the user (used for login). Shown on the default user card listing
password This is encrypted password for the user. It is a SHA1 hash of the random salt (below) and chosen password
salt A randomly generated hash to more securely encrypt the password
token The API token for this user. Changing this hash may break any APIs using this user to authenticate
access_token A Directus token/hash used to further secure the user's session identifiers, helping to prevent session hijacking
reset_token A unique token hash used to identify and authorize password change requests
reset_expiration The datetime that the above reset_token stops working – typically 24-48 hours after being emailed to the user's email address
position A plain text field for storing the user's position or role. Shown on the default user card listing
email_messages [0,1] A toggle for forwarding (1) or not forwarding (0) Directus messages to the user's email address
last_login A datetime value of the last time the user logged into Directus
last_access A datetime value of the last time the user was active within Directus
last_page The path to the last page the user visited within Directus. This allows users to start where they left off previously
ip The last known IP address the user accessed Directus from
group The ID of this user's User Group (from directus_groups). The chosen group determines this users access privileges
avatar The URL to an image avatar for this user. By default Directus saves the Gravatar image based on the email address
avatar_file_id An override to the above default, this sets the avatar to a file ID from within directus_files
location A plain text field for storing the user's location or office. Shown on the default user card listing
phone A plain text field for storing the user's phone number. Shown on the default user card listing
address A plain text field for storing the user's address
city A plain text field for storing the user's city
state A plain text field for storing the user's state
zip A plain text field for storing the user's zip code

Manually Setting Passwords

If you need to manually reset a user's password directly in the database you can use the following SQL snippet. Remember to replace the id value below with the actual ID of the user you wish to update.

UPDATE `directus_users` 
SET `salt` = SHA1(RAND()), `password` = SHA1(CONCAT(salt, 'new-password-here'))
WHERE `id` = 1

Directus Folder Structure & Key Files

This page gives a high-level overview of the files and folders of Directus.


Contains all server-side and lightly-abstracted database code.


Contains all client-side application code.


Contains all referenced less, js, css, font, and image files. The unbranded aspect of Directus means there should be no need to edit anything in this folder. All custom css and javascript should live within proprietary UIs, extensions, or list-views (see below).






Contains any proprietary extensions your project may use. Extensions are a catch-all for CMS functionality, if it can't be handled by Directus core or UIs, then extensions are a sandbox for any Each extension present here is automatically loaded into the system unless prepended with an underscore ("_example"). Extensions are self contained, so all templates, logic, etc is within its own folder – for initial setup use the provided example as a guideline... just remember to remove the prepended underscore to activate.


Contains all the necessary files to install and setup Directus. Once completed, this entire folder can and should be removed.


Contains custom templates for proprietary item listing pages. As a primer, reference the core list views within app/modules/xxx/views until a proper example list view is included.






Contains custom UIs (user interfaces) which are used as the inputs for data within the framework. Proprietary UIs in this folder are supplementary to the core UIs located within: app/core-ui – though the file structure/syntax is identical other than location. An exampleui.js file is included, but for more a more comprehensive understanding the core UIs should be perused. Any UIs added to this folder will be immediately available after refreshing the framework.

Hosted Guide

Your Account Dashboard

From here you can manage your account info, including your email, password, and payment details. From this panel you can also review your account's billing history, see any outstanding balance you might have, and close your account.

Hosted Instances

Creating Instances

Once you have added payment info into your account you can easily and quickly add new instances. Just click on the "Create Instance" button in the header, fill in an instance name, and choose a plan – that's it. Names should be lowercase alphanumeric with dashes instead of spaces – choose your instance name carefully as that will determine your database name and API URL. It should only take a few second for your database and Directus CMS to be set up. Then, once you've created a schema and content, your dynamic API will be available.

Managing Instances

Once you've created a few instances, you can easily manage them all in one place from the Instances section. In addition to changing your instance's plan (resizing), you will also be presented with other details, including:

You will also be able to see all of your API users/keys listed here. Each Directus user has an associated API key that inherits its data privileges. Keys can be be updated within the instance's Directus Users section.

Deleting Instances

You can delete instances from their detail page. There are three levels of confirmation to ensure you do not accidentally destroy instances. The following is permanently destroyed when deleting an instance: