To add text to an image in Laravel 13, install intervention/image, decode the image with an ImageManager, call $image->text() with the text, an x and a y position and a font callback, and then save or return the result. The callback sets the font file, size, color, stroke and alignment. This guide uses Intervention Image 4, whose API differs from version 3, and shows each option with working code.
Targets: Laravel 13 (PHP 8.3 to 8.5), intervention/image 4 (requires PHP 8.3 or newer) and the GD or Imagick PHP extension.
Step 1: Install the package
composer require intervention/imageYou need an image extension: GD is included in most PHP installs, Imagick is faster for large images. Check with php -m.
Step 2: Add a font file
Pick a font file you are allowed to use (for example from a free font library) and put it in your project, such as resources/fonts/Roboto-Bold.ttf. Setting a font file makes the text look the same on every server instead of depending on the fonts installed on the machine.
Step 3: Write text on an image
<?php
namespace App\Services;
use Intervention\Image\Alignment;
use Intervention\Image\Drivers\Gd\Driver;
use Intervention\Image\ImageManager;
use Intervention\Image\Typography\FontFactory;
class ImageTextService
{
public function write(string $sourcePath, string $targetPath, string $text): void
{
$manager = ImageManager::usingDriver(Driver::class);
$image = $manager->decode($sourcePath);
$image->text($text, (int) ($image->width() / 2), (int) ($image->height() - 60), function (FontFactory $font) {
$font->filepath(resource_path('fonts/Roboto-Bold.ttf'));
$font->size(48);
$font->color('ffffff');
$font->stroke('000000', 2);
$font->align(Alignment::CENTER, Alignment::BOTTOM);
});
$image->save($targetPath);
}
}The first three arguments of text() are the string and the x and y coordinates of the base point. Here the point is the horizontal center near the bottom of the image, and align() centers the text around that point.
What do the font options do?
filepath()sets the path of the font file.size()sets the font size (the default is 12).color()sets the text color in any supported color format, such as'fff','ffffff'or'rgb(255, 255, 255)'.stroke()adds an outline with a color and a width from 0 to 10. When stroke is used, the text and stroke colors must be fully opaque.align()sets horizontal and vertical alignment relative to the point you passed.lineHeight(),angle()andwrap()control line spacing, rotation and the maximum width in pixels before text wraps to a new line.
Step 4: Use it from a controller
<?php
namespace App\Http\Controllers;
use App\Services\ImageTextService;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Str;
class ImageTextController extends Controller
{
public function store(Request $request, ImageTextService $service): RedirectResponse
{
$data = $request->validate([
'image' => ['required', 'image', 'mimes:jpg,jpeg,png,webp', 'max:5120'],
'text' => ['required', 'string', 'max:80'],
]);
$name = Str::uuid()->toString().'.'.$request->file('image')->extension();
$target = storage_path('app/public/watermarked/'.$name);
if (! is_dir(dirname($target))) {
mkdir(dirname($target), 0775, true);
}
$service->write($request->file('image')->getRealPath(), $target, $data['text']);
return back()->with('success', 'Saved as storage/watermarked/'.$name);
}
}The save() method needs an existing folder, so the controller creates it first. The format of the saved file follows the file extension in the target path.
How do I return the image without saving it?
Encode it and send it in the response:
use Intervention\Image\Format;
$encoded = $image->encodeUsingFormat(Format::JPEG, quality: 85);
return response((string) $encoded, 200, ['Content-Type' => 'image/jpeg']);How do I check the result?
- Upload a photo with the text "Hello Laravel".
- Open the saved file and confirm the text appears at the bottom center with an outline.
- Change the size, color and alignment values and repeat to see their effect.
Common mistakes
- Using the version 3 API: version 4 creates the manager with
ImageManager::usingDriver(Driver::class)and loads files withdecode(), notread(). - Wrong font path: the text silently looks wrong or an error occurs. Check the file exists.
- Text too long for the image: use
wrap()or reduce the size. - Missing target folder:
save()does not create folders. - Trusting user file names: generate a new name as the controller does.
FAQ
Does this work with the Imagick driver?
Yes. Change the import to Intervention\Image\Drivers\Imagick\Driver. The text methods stay the same.
Can I add text to a transparent PNG?
Yes, but if you use stroke(), remember the text and stroke colors must be fully opaque.
Is there a Laravel integration package?
Yes, intervention/image-laravel adds a facade and a config file. The core API above is the same.



