Manage any Laravel app as if it was a CMS

We all love working with Laravel but what if we could take a step further – imagine that we could edit any page or any email of our Laravel app as if it was some kind of a CMS. We see a rendered HTML page where we can click on any element and just edit it, instead of going to the Blade templates or language files?

As a consequence, we could invite our non-dev colleagues to collaborate – we could discuss, manage and deploy text updates together, our colleagues wouldn’t be blocked by developers anymore. But even if you are a single developer shop, the value is still lucrative – being able to see all texts in their real context, quickly navigate through different scenarios and edge cases without touching the database or seeding or anything of that sort.

Are you 100% sure that all your Blade templates and language files are completely in sync with the product’s current state and that nowhere, nowhere in your UX you say things to customers that aren’t right anymore? Are you 100% sure that all pages have SEO-optimised HTML meta tags?
Are you 100% sure that all texts have corresponding translations in all product languages and they fit nicely within their UX elements?

A new paradigm

Paragraph offers a new paradigm – instead of managing view templates, language files, translation keys and all that other boring stuff, let’s try to think about our product from a higher level, from a customer level really – let’s start working with pages, emails and user journeys instead!

After all, users don’t land on “welcome.blade.php”, they land on Welcome page. They don’t create accounts on “auth.form”, they create accounts by filling a form on the Sign Up page. Calling things what they are is going to make it much easier to manage the product, for both developers and non-developers.

Getting started

In this example we will use a brand new Laravel app and work with its welcome page – the page that most of us are so familiar with 🙂 Let’s create an app using composer:

1$ composer create-project laravel/laravel laravel-test

2$ cd laravel-test

Now we head to Paragraph to create a new project (free!):

And now that we have received our API key and saved it in the .env file, we can install Paragraph’s composer package and then launch a local web server:

1$ composer require paragraph/laravel

2$ php artisan serve

Opening our brand new Laravel app in the browser shows this cute welcome page:

If you look at welcome.blade.php, you will notice that all texts are directly inside the HTML markup:

1<div class="ml-4 text-lg leading-7 font-semibold">

2 <a href="" class="underline text-gray-900 dark:text-white">

3 Documentation

4 </a>


Let’s wrap them in Laravel’s localization directive so that they become translatable:

1<div class="ml-4 text-lg leading-7 font-semibold">

2 <a href="" class="underline text-gray-900 dark:text-white">

3 @lang('Documentation')

4 </a>


This is a very safe thing to do and it won’t break anything regardless on whether you actually plan to translate your product or not. Laravel will always fall back to the original text if there is no translation available. This also means that you will not be tied to Paragraph, there is no lock-in. You are using a framework’s feature and you are free to swap your specific implementations at any time.

Now lets clear the cache by running php artisan paragraph:clear-views and then visit the same page again in browser:

Looks about the same? Well, it’s the same page. But now, with our Paragraph hook incorporated in the Laravel’s rendering mechanism, the view got analysed and saved as a snapshot in a temporary folder. We can submit all outstanding snapshots by running a console command:

1$ php artisan paragraph:submit-views

2Submitting storage/paragraph_view_snapshot_welcome.html

3Submitted a total of 1 snapshots

If we go back to the Paragraph dashboard, we will find out that there is 1 page available to manage now:

If we click on “Pages” we can see a list of all our product pages. Currently it’s just the Welcome page that we imported a few seconds ago:

Click on the page to open a full page editor. As we can see, all texts are editable in-place now, CMS-style editing! Paragraph just turned our Laravel app into WordPress, but in a good way.

From this moment, we don’t have to go back to the language files or Blade templates, we can just stay here, we can use Paragraph for any copy updates. As long as our @lang directives are in place, we can control the texts from a cloud dashboard that will allow us to be way more productive.

To bring the updates back to your Laravel app, we need to download the latest copy updates from time to time by running a console command either manually, via cron or as a part of your CI/CD pipeline:

1$ php artisan paragraph:download


If you need to localise your Laravel product – to translate it into different languages, then you just found a perfect package for that. Just add any languages you need in the project settings on Paragraph and you can start translating right away. You have two editor options – you can use the page view that we shown above, or you can open a more “traditional” table view with all texts.

To help you progress faster, we included AWS, Bing and Google Cloud machine translators that can provide you with a starting point:

We only made two clicks and now the progress of our Spanish translations moved to 100%:


Your product might be the best in the world but people still need to be able to find it in order to become your customers. Having relevant, SEO-optimised HTML tags is crucial but not so easy to achieve when you have hundreds or thousands of Blade templates scattered around the repository.

Let’s wrap our HTML tags in the Laravel’s translation directive:


2 name="description"

3 content="@lang('A very efficient SEO description goes here')"



6 name="keywords"

7 content="@lang('SEO keywords go here')"



Let’s now clear the cache, refresh the page in browser and then re-submit the data to Paragraph:

1$ php artisan paragraph:clear-views

2Deleted storage/paragraph_view_snapshot_welcome.html

3Deleted a total of 1 snapshots

1$ php artisan paragraph:submit-views

2Submitting storage/paragraph_view_snapshot_welcome.html

3Submitted a total of 1 snapshots

Great, now the 3 SEO tags we just added are in Paragraph as well:

We can manage all our SEO tags (as well as translate them if necessary) in a breeze!

Going advanced

You probably noticed how we had to open the local page in browser before we could submit anything to the cloud. That’s the easiest way to start because it doesn’t require any additional work, a small website could even enable this in production – capture different pages and then submit them from time to time to Paragraph using cron.

But if you want to have a better control, a nicer way to do this is by incorporating the special Paragraph‘s trait in your test suite:

1use Paragraph\WithParagraph;


3class ExampleTest extends TestCase


5 use RefreshDatabase, WithFaker, WithParagraph;


This way, every time you run tests locally or in your CI/CD pipeline, pages will be updated to their new design and new texts will automatically be parsed.

On top of that, you can define user journeys – sequences of steps and outgoing communications:


2 * @test

3 */

4public function journey()


6 $this->sequence('User creates an account', function () {

7 $this->name('Home page')->get('/');

8 $this->name('Sign-up page')->get('/sign-up');

9 $this->post('/sign-up', ['email' => '', 'password' => '']);

10 // Behind the scenes a WelcomeEmail($user) mailable is sent

11 });


This will automatically create a “User creates an account” journey on Paragraph consisting of 2 steps in browser and one outgoing welcome email.

Interested? Does this sound neat to you? You can connect your Laravel app by creating a free account on Paragraph today.

Source link


Leave a Reply

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