How to Install and Use Trix Editor in Laravel

To manage content on the website, you always require a rich text editor aka WYSIWYG editor. The rich text content includes HTML tags that can be added only through the WYSIWYG editor. The textarea tag can’t handle content wrapped inside HTML.

There are numerous rich text editors(TinyMCE, CKEditor) available on the Internet. Trix Editor is one of them. It is developed by Basecamp. The Trix editor is being used in Basecamp for managing content. This is enough to say about the reliability of this WYSIWYG editor.

The purpose of this article is to show you how to install and use the Trix editor in Laravel. I’ll also explain how to upload images in the Trix editor. Following this tutorial, you’ll be able to handle rich text content in your Laravel application.

Installation of Trix Editor

For getting started with Trix, you first need to include their JS and CSS files. Get these files from the below URLs.

To include the above assets, create a js and css folder inside the public directory of your Laravel project. Place the copied files in their respective directories.

Uploading images on the server using Trix requires writing some code in JavaScript. Create the attachments.js file and keep it under the public/js folder. We will add JavaScript code to this file in the later part of the tutorial.

Next, let’s define the routes in Laravel. We have to achieve 3 tasks.

  • Display Trix Editor
  • Upload images in Trix Editor
  • Handle content submitted via Trix Editor

The following routes will handle these tasks.

Route::get('/trix', 'TrixController@index');
Route::post('/upload', 'TrixController@upload');
Route::post('/store', 'TrixController@store');

Create a TrixController using the Artisan command:

php artisan make:controller TrixController

The boilerplate of TrixController would be as follows.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TrixController extends Controller
{
    public function index()
    {
        return view('trix');
    }

    public function store(Request $request)
    {
    }

    public function upload(Request $request)
    {
    }
}

Display Trix Editor

Now, create a trix.blade file and add the following HTML to it. It will display the Trix editor.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <link rel="stylesheet" href="{{ asset('/css/trix.css') }}">
</head>
<body>
    <form method="post" action={{ url('store') }}>
        @csrf
        <p>
            <input id="x" type="hidden" name="content" value="" />
            <trix-editor input="x" class="trix-content"></trix-editor>
        </p>
        <input type="submit" name="submit" value="Submit" />
    </form>
     
    <script src="{{ asset('js/trix.umd.min.js') }}"></script>
    <script src="{{ asset('js/attachments.js') }}"></script>
</body>
</html>

After this, your Trix editor will be shown something like the screenshot below.

Trix Editor

We placed the Trix editor in HTML using the trix-editor tag. We also added the hidden field that will be used to get or show content in the Trix editor. This hidden field has the id “x” which is the same as the editor’s input attribute. When you type anything inside Trix, the content is set to the value of this hidden field. On the server side, you can get the content of the Trix editor as follows.

public function store(Request $request)
{
    echo $request->input('content');
}

If you want to populate content in Trix, set the value to the hidden field as

<input id="x" type="hidden" name="content" value="<h1>This is content</h1>" />

Upload Image in Trix Editor

To add images in the Trix editor, you need to upload them on the server. Trix editor provides an event trix-attachment-add through which you can send the image to the server via Ajax. We have already added the upload route for it. Let’s define a method mapped with this route.

public function upload(Request $request)
{
    if($request->hasFile('file')) {
        //get filename with extension
        $filenamewithextension = $request->file('file')->getClientOriginalName();

        //get filename without extension
        $filename = pathinfo($filenamewithextension, PATHINFO_FILENAME);

        //get file extension
        $extension = $request->file('file')->getClientOriginalExtension();

        //filename to store
        $filenametostore = $filename.'_'.time().'.'.$extension;

        //Upload File
        $request->file('file')->storeAs('public/uploads', $filenametostore);

        // you can save image path below in database
        $path = asset('storage/uploads/'.$filenametostore);

        echo $path;
        exit;
    }
}

Here, I am storing images under the public/uploads directory. So create a symbolic link to the storage folder using the command:

php artisan storage:link

The attachments.js file will be used to give an Ajax call on the trix-attachment-add event. The following code will handle the Ajax request and response.

attachments.js

(function() {
    var HOST = "http://localhost:8000/upload"; //pass the route
 
    addEventListener("trix-attachment-add", function(event) {
        if (event.attachment.file) {
            uploadFileAttachment(event.attachment)
        }
    })
 
    function uploadFileAttachment(attachment) {
        uploadFile(attachment.file, setProgress, setAttributes)
 
        function setProgress(progress) {
            attachment.setUploadProgress(progress)
        }
 
        function setAttributes(attributes) {
            attachment.setAttributes(attributes)
        }
    }
 
    function uploadFile(file, progressCallback, successCallback) {
        var formData = createFormData(file);
        var xhr = new XMLHttpRequest();
         
        xhr.open("POST", HOST, true);
        xhr.setRequestHeader( 'X-CSRF-TOKEN', getMeta( 'csrf-token' ) );
 
        xhr.upload.addEventListener("progress", function(event) {
            var progress = event.loaded / event.total * 100
            progressCallback(progress)
        })
 
        xhr.addEventListener("load", function(event) {
            var attributes = {
                url: xhr.responseText,
                href: xhr.responseText + "?content-disposition=attachment"
            }
            successCallback(attributes)
        })
 
        xhr.send(formData)
    }
 
    function createFormData(file) {
        var data = new FormData()
        data.append("Content-Type", file.type)
        data.append("file", file)
        return data
    }
 
    function getMeta(metaName) {
        const metas = document.getElementsByTagName('meta');
       
        for (let i = 0; i < metas.length; i++) {
          if (metas[i].getAttribute('name') === metaName) {
            return metas[i].getAttribute('content');
          }
        }
       
        return '';
      }
})();

I have assigned the upload route to the HOST variable. Laravel requires a CSRF token for each request. I am fetching this token from the meta tag added in the blade file. When you add the image in Trix, it triggers the Ajax request, uploads the image on the server, and returns the path of the uploaded image. This image then appends in the Trix editor. You can add styling to these images using the trix-content class of the trix-editor tag.

.trix-content img {
    width: 300px;
    height: 300px;
}

I hope you understand how to install and use the Trix editor in Laravel. Check out the documentation to read more about the Trix editor.

If you liked this article, then please subscribe to our YouTube Channel for video tutorials.

1 thought on “How to Install and Use Trix Editor in Laravel

  1. Hallo, this has been an excelent tutorial.
    I’d like to know te proper way to remove the file listening to the ” trix-attachment-remove ” event. I’m using Laravel 8.
    Also, I’d like to store the path with te foreignKey related, in order to be able to authorize the access to the corresponding user.
    As you might imagine, I feel confortable with Laravel and PHP, but not so much bith javascript.
    Thanks anyway. Best regards. Hern’an.

Leave a Reply

Your email address will not be published. Required fields are marked *