Upgrade from 5 to 6
A guide for upgrading from 5 to 6. For most sites (those running Laravel > 10), the process will take less than 5 minutes.
Overview
First read through this guide to see if there's anything that you might need to adjust. While there are many items on this page, a majority of them only apply to addons or custom code. We've noted who each item would apply to so you can more easily scan through the changes.
Upgrade using Composer
In your composer.json
, change the statamic/cms
requirement:
-"statamic/cms": "^5.0"
+"statamic/cms": "^6.0"
Then run:
composer update statamic/cms --with-dependencies
High impact changes
PHP and Laravel support
Affects apps using PHP < 8.2 or Laravel < 11.
- The minimum version of PHP is now 8.2.
- The minimum version of Laravel is now 11.
We highly recommend upgrading all the way to Laravel 12 and PHP 8.4.
If you want to (semi-)automate the Laravel upgrade process, we recommend using Laravel Shift (use that link for a special 19.83% discount 🤘).

Vue 3
Affects apps or addons that use Vue.
We have upgraded the Control Panel's version of Vue.js from 2 to 3.
To keep this upgrade guide manageable, we have a dedicated page for upgrading from Vue 2 to Vue 3.
If you do not have any custom Vue components in your app, or in your own addons, you can skip this.
Timezones
Affects apps using dated collections or date fields
If your timezone
setting in config/app.php
is set to UTC
, then nothing will change for you.
Dates remain stored in your application's timezone. But now Statamic will convert them to UTC at runtime, which makes it much easier for Statamic to localize them as needed.
This applies to dated entries or date fields.
For example, if you have your timezone set to New York (GMT-5:00) and you have a date at 10pm, when it gets converted to UTC it will be 5 hours ahead - in the next day!
// config/app.php
'timezone' => 'America/New_York',
# an-entry.md
my_date_field: '2025-03-06 22:00'
$entry->my_date_field;
-// 5.x: Carbon { 2025-03-06 22:00 America/New_York }
+// 6.x: Carbon { 2025-03-07 03:00 UTC }
{{ my_date_field | iso_format('JJJJ') }}
{{ Statamic::modify($my_date_field)->iso_format('JJJJ') }}
-5.x: Thursday, March 6, 2025 10:00 PM +6.x: Friday, March 7, 2025 3:00 AM
It's best practice to keep dates as UTC until you're ready to display them, which means modifiers will deal with UTC versions. But, you can opt into automatic conversion to your display timezone by changing the following in config/statamic/system.php
:
+'localize_dates_in_modifiers' => true,
This settings should have been automatically set to true
by Statamic during the upgrade, but you should confirm it.
Control Panel
Dates in the Control Panel are now localized to the user's operating system timezone, rather than the application timezone.
For example, on Statamic 5, if you were in a different timezone to what your app was configured in, and you select a date from the date picker, that date would be treated as the date for the app's timezone. Not your timezone.
This was a common cause of confusion, which was one of the main reasons for all these changes.
Now in Statamic 6, the date you pick will be the date in your timezone.
There is nothing for you to change except your expectations when working with dates, and instructing your clients about it.
REST API & GraphQL
Dates will now be returned by Statamic's REST API and GraphQL API in UTC, allowing you to localize them as needed on your frontend.
Changing your app timezone
It's best practice to set your app's timezone to UTC. However, changing the timezone in an existing project is a big undertaking and could mean lots of content and dates need to be updated.
Statamic 6 does not require that you change your timezone to UTC. But if you want to, we have provided a way to automate it.
Read how to change your timezone to UTC.
Medium impact changes
Carbon 3
Support for Carbon 2.x has been removed. All Statamic 6 sites now require Carbon 3.x.
If you're using any of Statamic's months_ago
, weeks_ago
, days_ago
, hours_ago
, minutes_ago
, and seconds_ago
modifiers, you will notice that they now return floats instead of integers. Comparing against past timestamps will also result in negative numbers.
You may need to updates your templates to account for these changes.
Globals
We have made various changes to how globals are stored and localized. If you use globals in your app, please read through these changes and take any necessary action.
Single site installs: Variables are now stored separately from the global set config
Global Variables are now stored separately from the global set's config, allowing config and content to be properly separated.
Instead of living under a data
key in the global set's YAML file, they now live in a separate YAML file in a directory named after your default site, usually called default
.
Before:
# content/globals/seo.yaml
title: SEO
data:
meta_description: Synthwave nostalgia with soaring sax and heartfelt vibes.
meta_image: the-midnight.jpg
After:
# content/globals/seo.yaml
title: SEO
# content/globals/default/seo.yaml
meta_description: Synthwave nostalgia with soaring sax and heartfelt vibes.
meta_image: the-midnight.jpg
This change may have been performed automatically by Statamic during the upgrade process.
Note: This change doesn't affect multi-sites or sites storing global variables in the database, since they're already stored separately.
Multi-sites: Localized sites are now determined by the sites
array
Previously, when you configured the sites a global set was localized into, it created the global variable files for you, then used the existence of those files to determine which sites the global set was localized into.
Now, Statamic will use the sites
array in the global set's config file to determine which sites the global set is localized into, as well as mapping the origins for localizations.
# content/globals/seo.yaml
title: SEO
sites:
en: null
fr: en # Localized from en
de: null # No origin
This change may have been performed automatically by Statamic during the upgrade process.
Note: This change doesn't affect single-site installs.
Events
Previously, when saving global variables in the Control Panel, the entire global set would have been saved, causing the GlobalSetSaving
, GlobalSetCreated
and GlobalSetSaved
events to be dispatched. However, now, only the global variable itself will be saved.
This means that if you were listening to any of these events to pick up changes to global variables, you should instead listen for the GlobalVariablesSaving
, GlobalVariablesCreated
and GlobalVariablesSaved
events.
Removed methods on GlobalSet
class
The addLocalization
and removeLocalization
methods have been removed from the GlobalSet
class.
If you were calling these methods in your app, you should update your code to call save
and delete
on the Variables
class instead.
-$globalSet->addLocalization($globalSet->makeLocalization('en')->data(['foo' => 'bar'])); -$globalSet->removeLocalization('en');
+$globalSet->in('en')->data(['foo' => 'bar'])->save(); +$globalSet->in('en')->delete();
Breadcrumbs
Affects apps or addons displaying breadcrumbs in the Control Panel.
Breadcrumbs are now generated from items in the Control Panel navigation, rather than needing to be passed into views manually.
You should remove references to the Breadcrumb
class in your code, as well as the <breadcrumbs>
Vue component.
-use Statamic\CP\Breadcrumbs; -
-$crumbs = Breadcrumbs::make([
- ['text' => 'First', 'url' => '/first'],
- ['text' => 'Second', 'url' => '/second'],
-]);
-
-return view('myview', ['crumbs' => $crumbs]);+return view('myview');
-<breadcrumbs :crumbs='@json($crumbs)'></breadcrumbs>
<template>
- <breadcrumbs :crumbs="crumbs" /> </template>
<script>
export default {
data()
return {
- crumbs: [ - ['text' => 'First', 'url' => '/first'],
- ['text' => 'Second', 'url' => '/second'],
- ] ]
}
}
</script>
To learn more about customizing breadcrumbs, please refer to the CP Navigation documentation.
Starter Kits: Removed export_as
option
Affects starter kits using the export_as
option.
The export_as
option has been removed in favor of the new package
folder convention, which makes dealing with multiple README.md files easier, for example.
Low impact changes
Added columns to the users
table
Affects apps storing users in the database.
If you're storing users in the database, you will need to add three columns to the users
table in order to support Statamic's two-factor authentication feature. You can add the columns using a migration:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->text('two_factor_secret')->nullable();
$table->text('two_factor_recovery_codes')->nullable();
$table->timestamp('two_factor_confirmed_at')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['two_factor_secret', 'two_factor_recovery_codes', 'two_factor_confirmed_at']);
});
}
};
You can run the migration by running php artisan migrate
.
You should also add a cast for the two_factor_confirmed_at
column in your User
model:
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'preferences' => 'json',
+ 'two_factor_confirmed_at' => 'datetime', ];
}
This change may have been performed automatically by Statamic during the upgrade process.
Moment.js has been removed
Affects addons and custom code directly using moment.js
If you are using moment.js you should replace it with an alternative. We suggest using native JS code which should be enough these days. For example:
-moment().seconds(); +new Date().getSeconds();
You should search for moment
or $moment
references in your code and replace appropriately.
Here is a good resource on how to migrate away from Moment.js.
Glide 3
Statamic is now using Glide 3, which uses intervention/image
3.x under the hood.
In most cases, you shouldn't notice any difference, however, if you have a custom manipulator, it will need to be updated. Please refer to the Glide 3 changelog for more information.
Wildcard tags
Affects apps using the {{ session }}
, {{ cookie }}
, {{ nav }}
and {{ redirect }}
tags.
In previous versions of Statamic, these tags accepted a wildcard value, allowing you to pass a handle or key directly:
{{ session:foo }}
Here, foo
is the wildcard value. However, if a variable named foo existed in the template’s context, its value would be used instead of the literal string "foo"
, potentially causing unintended behaviour.
In Statamic 6, wildcard values are always treated as literal strings. If you need to pass a variable dynamically, you should use the appropriate parameter instead:
{{ session :handle="foo" }}
This ensures that foo
is interpreted as a variable rather than a fixed string.
Site methods
Affects addons using the Site::setConfig()
method.
The Site::setConfig()
method was deprecated in Statamic 5. It has now been removed. You should use the Site::setSites()
method instead:
-Site::setConfig([ - 'sites' => [
- 'english' => ['name' => 'English', 'locale' => 'en_US', 'url' => '/en'],
- 'french' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => '/fr'],
- ],
-]);
+Site::setSites([ + 'english' => ['name' => 'English', 'locale' => 'en_US', 'url' => '/en'],
+ 'french' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => '/fr'],
+]);
CP Nav Items: ->active()
method removed
Affects apps or addons adding nav items to the Control Panel.
When adding nav items to the Control Panel, it was previously possible to specify a regex pattern used to determine if the nav item was active. However, after some improvements in Statamic, this method is no longer needed and has been removed after a deprecation period. You can safely remove it from your nav items:
Nav::extend(function ($nav) {
$nav->create(ucfirst($type))
->section('SEO')
->route("ecommerce.orders.index")
- ->active("ecommerce/orders") ->icon($defaults->first()['type_icon']);
});
statamic
cache driver has been removed
Affects apps or addons using the statamic
cache driver.
The statamic
cache driver has been removed. If you were using it, you should switch to the file
cache driver instead.
// config/cache.php
-'driver' => 'statamic', +'driver' => 'file',
Entry::addLocalization()
method removed
Affects apps or addons using the Entry::addLocalization()
method.
The Entry::addLocalization()
method has been removed. If you were using it, you should now use the Entry::makeLocalization()
method instead.
-$entry->addLocalization($entry); +$entry->makeLocalization('german');
ApiController::filterSortAndPaginate()
method renamed
Affects apps or addons providing custom API endpoints.
The filterSortAndPaginate()
method on the ApiController
class has been renamed to updateAndPaginate()
.
-$this->filterSortAndPaginate($query); +$this->updateAndPaginate($query);
Relate tag has been removed
The relate
tag left over from Statamic 2 has been removed. You can safely remove it and rely on augmentation instead.
-{{ relate:products }} - {{ title }}
-{{ /relate:products }}
+{{ products }} + {{ title }}
+{{ /products }}
docs-callout
partial has been replaced
Affects apps or addons with custom Blade views in the Control Panel.
Statamic's docs-callout
partial has been replaced with a Blade component of the same name. If you were using this partial in your custom Blade views, you should update your code to use the component instead.
Before:
@include(
'statamic::partials.docs-callout',
[
'topic' => __('Blueprints'),
'url' => Statamic::docsUrl('blueprints'),
]
)
After:
<x-statamic::docs-callout
:topic="__('Blueprints')"
:url="Statamic::docsUrl('blueprints')"
/>
Section fieldtype has been deprecated
The Section fieldtype has been deprecated and will be removed in Statamic 7. We recommend using sections in the blueprint builder instead.
urlencode
and rawurlencode
modifiers now encode forward slashes
Affects apps or addons using the urlencode
or rawurlencode
modifiers.
The urlencode
and rawurlencode
modifiers now encode forward slashes (/
). If you were relying on forward slashes not being encoded, you can use the urlencode_except_slashes
and rawurlencode_except_slashes
modifiers instead.
{{ my_string | urlencode }} // [tl! --]
{{ my_string | urlencode_except_slashes }} // [tl! ++]
{{ my_string | rawurlencode }} // [tl! --]
{{ my_string | rawurlencode_except_slashes }} // [tl! ++]
Zero impact changes
...
Docs Feedback
Submit improvements, related content, or suggestions through Github.
Betterify this page