Laravel 12 - How to Upload Images in CKEditor.
Laravel 12 - How to Upload Images in CKEditor.
In this tutorial, we will learn how to upload images in CKEditor using Laravel 12. We will cover setup, file upload handling, and storage for a complete working solution.
Quick Overview
This guide walks you through integrating CKEditor with image upload functionality in a Laravel project. It starts by creating a fresh Laravel 12 project with MySQL and Pest configured for testing. You’ll then create the Post model and migration, followed by the PostController to manage displaying posts, saving new entries, and handling image uploads from CKEditor. Routes are defined to link the frontend form and editor to the controller, and the Blade view is updated with a Tailwind styled CKEditor form for rich text and images. After creating a symbolic link to make uploaded files publicly accessible, you’ll test the setup by running Laravel’s development server and verifying that images upload correctly. This setup ensures seamless handling of both text and media in your Laravel application.
Step # 1 : Create a Fresh Laravel 12 Project.
Before we begin, it’s a good idea to start with a clean Laravel 12 installation. A fresh project keeps everything straightforward and helps avoid unnecessary conflicts. If you already have the Laravel Installer installed globally, you can create a new project quickly by running.
laravel new ckeditor
Alternatively, you can use Composer.
composer create-project laravel/laravel --prefer-dist ckeditor
When using Composer, the project is created immediately without any prompts, and you can configure everything later via the .env file. In contrast, the Laravel Installer provides a short interactive setup process. If you use the Installer, choose the following options during setup.
- Starter Kit: Select None this keeps the project simple without authentication scaffolding.
- Testing Framework: Select Pest a modern and developer friendly choice.
- Database: Select MySQL we’ll use this for our project data.
- Run migrations: Slect No, as we will be creating migrations related to post.
- Frontend Dependencies: Select Yes to install frontend tools automatically.
After completing the setup, you’ll have a fresh Laravel 12 project named ckeditor with Pest, MySQL, and no starter kit preconfigured based on your selection.
Step # 2 : Move Into Your Project Folder.
Next, open your terminal (for example, Git Bash) and navigate to the root directory of your Laravel project. For example.
cd c:/xampp/htdocs/ckeditor
Make sure you’re inside the project folder before running any Laravel or Artisan commands.
Step # 3 : Set Up the Post Model and Database Table.
Now let’s create a Post model along with its migration file.
php artisan make:model Post -m
Next, open the generated migration file (create_posts_table) and update it by adding a body column.
<?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::create('posts', function (Blueprint $table) {
$table->id();
$table->text('body');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts');
}
};
After that, update the Post model to allow mass assignment for the body field.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = [
'body',
];
}
Finally, run the migration.
php artisan migrate
If the database does not exist yet, Laravel will prompt you with a warning like.
WARN The database 'ck' does not exist on the 'mysql' connection.
Would you like to create it? (yes/no) [yes]
Simply type yes to proceed, and your posts table will be created with a body column, making the model ready for use.
Step # 4 : Build the Post Controller Logic.
Next, generate a controller for handling posts.
php artisan make:controller PostController
Once created, update the PostController with the following logic.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Post;
use Illuminate\Support\Facades\Storage;
class PostController extends Controller
{
// Display all posts on the welcome page
public function index()
{
$posts = Post::all();
return view('welcome', compact('posts'));
}
// Store a new post in the database
public function store(Request $request)
{
$request->validate([
'body' => 'required',
]);
Post::create([
'body' => $request->body,
]);
return redirect()->back()->with('success', 'Post created successfully!');
}
// Handle image uploads for CKEditor
public function uploadImage(Request $request)
{
if ($request->hasFile('upload')) {
$request->validate([
'upload' => 'image|mimes:jpeg,png,jpg,gif|max:2048',
]);
$path = $request->file('upload')->store('uploads', 'public');
$url = Storage::url($path);
return response()->json([
'uploaded' => true,
'url' => $url,
]);
}
return response()->json(['uploaded' => false], 400);
}
}
This controller takes care of listing posts, saving new entries, and handling image uploads for CKEditor. The index() method retrieves all posts, store() validates and stores user input, and uploadImage() manages image uploads by validating, storing, and returning the file URL.
Step # 5 : Set Up Application Routes.
Open the web.php file and register the routes for your application. First, import the PostController at the top.
use App\Http\Controllers\PostController;
Then, define the routes to handle displaying posts, saving new entries, and processing CKEditor image uploads.
// Display all posts on the home page
Route::get('/', [PostController::class, 'index']);
// Store a new post
Route::post('/store', [PostController::class, 'store']);
// Handle CKEditor image uploads
Route::post('/upload/image', [PostController::class, 'uploadImage']);
These routes connect your frontend to the controller methods (/) displays all posts on the homepage, (/store) handles form submissions for creating new posts, and (/upload/image) manages image uploads from CKEditor.
Step # 6 : Update the Welcome Blade View.
Update the welcome.blade.php with the following code. This adds a post submission form using CKEditor for rich text editing, allows image uploads, and displays submitted posts below the form. Images are styled to fit neatly within the layout using Tailwind CSS.
<!DOCTYPE html>
<html>
<head>
<title>Laravel 12 - CKEditor (Upload Image) Example</title>
<!-- Tailwind CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- CKEditor CDN -->
<script src="https://cdn.ckeditor.com/ckeditor5/41.4.2/classic/ckeditor.js"></script>
<!-- CKEditor editable area styling -->
<style>
.ck-editor__editable {
background-color: #ffffff !important;
color: #000000 !important;
min-height: 100px;
}
</style>
</head>
<body class="bg-gray-900 text-gray-100">
<div class="max-w-4xl mx-auto mt-10 px-4">
<form action="{{ url('store') }}" method="POST" enctype="multipart/form-data" class="mb-6 bg-gray-800 p-6 rounded-xl shadow">
@csrf
<div class="mb-4">
<label for="body" class="block mb-2">Body:</label>
<!-- This textarea will be replaced by CKEditor dynamically -->
<textarea id="body" name="body" class="w-full rounded-md bg-white text-black"></textarea>
</div>
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 px-5 py-2 rounded-md">
Submit
</button>
</form>
<!-- Display the posts section only when posts exist -->
@if($posts->isNotEmpty())
<div class="mt-6 p-6 bg-gray-800 rounded-xl shadow">
<h1 class="text-xl mb-4">All Posts</h1>
@foreach($posts as $post)
<div class="mb-4">
<strong>Post ID:</strong> {{ $post->id }} <br>
<div class="prose prose-invert max-w-none
prose-img:max-w-full
prose-img:h-auto">
{!! $post->body !!}
</div>
</div>
@endforeach
</div>
@endif
</div>
<!-- CKEditor configuration -->
<script>
// Initialize CKEditor on the #body textarea
ClassicEditor
.create(document.querySelector('#body'), {
ckfinder: {
// URL used for image uploads. CSRF token is required for Laravel authentication
uploadUrl: '{{ url("/upload/image").'?_token='.csrf_token() }}'
}
})
.catch(error => {
// Log errors for debugging. could be enhanced to show user-friendly UI notifications
console.error(error);
});
</script>
</body>
</html>
The view presents a clean, Tailwind styled form with CKEditor for creating rich text posts, including image uploads. Posts submitted through the form are listed below, with all images automatically sized to fit neatly within their containers using Tailwind’s prose-img utilities.
Step 7 : Create a Symbolic Link for Public Storage.
To make uploaded images and files (like those from CKEditor) accessible in the browser, you need to create a symbolic link between Laravel’s storage folder and the public directory. Run the following command.
php artisan storage:link
This will link public/storage to storage/app/public, allowing any files stored there to be served via a public URL. Without this link, uploaded files won’t be accessible through the browser.
Step # 8 : Test Your CKEditor Image Uploads.
Now it’s time to make sure everything works as expected. Start Laravel’s development server by running.
php artisan serve
Then, open your browser and go to: http://127.0.0.1:8000. Try uploading images through CKEditor. If everything is set up correctly, your images should upload and display without any issues. This confirms that the editor, storage link, and server are all working together.
Conclusion
By following this guide, you’ve successfully added CKEditor to your Laravel application with full support for image uploads. Your app can now handle rich text content alongside media, making posts more interactive and visually appealing. With the storage symlink in place, uploaded images are accessible seamlessly, and the CKEditor integration allows easy management of both text and media within posts. From here, you can further customize CKEditor, explore its advanced features, and integrate additional tools to enhance your application’s functionality.
For deeper insights and options, check the official CKEditor documentation.
For deeper insights and options, check the official CKEditor documentation.
Share this with friends!
To engage in commentary, kindly proceed by logging in or registering
Subscribe to Our Newsletter
Stay ahead of the curve! Join our newsletter to see what everyone’s talking about.
0 Comments