Craft the perfect image

Crop Photos with Cropper.js

Let your users crop images with Cropper.js then grab the final image from PHP. Simple.

composer require symfony/ux-cropperjs
// ... use statements hidden - click to show
use App\Service\UxPackageRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Asset\Packages;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\UX\Cropperjs\Factory\CropperInterface;
use Symfony\UX\Cropperjs\Form\CropperType;

class CropperjsController extends AbstractController
{
    public function __construct(
        private Packages $assets,
        #[Autowire('%kernel.project_dir%')] private string $projectDir,
    ) {
    }

    #[Route('/cropperjs', name: 'app_cropperjs')]
    public function __invoke(UxPackageRepository $packageRepository, CropperInterface $cropper, Request $request): Response
    {
        $package = $packageRepository->find('cropperjs');

        $crop = $cropper->createCrop($this->projectDir.'/assets/images/large.jpg');
        $crop->setCroppedMaxSize(1000, 750);

        $form = $this->createFormBuilder(['crop' => $crop])
            ->add('crop', CropperType::class, [
                'public_url' => $this->assets->getUrl('images/large.jpg'),
                'cropper_options' => [
                    'aspectRatio' => 4 / 3,
                    'preview' => '#cropper-preview',
                    'scalable' => false,
                    'zoomable' => false,
                ],
            ])
            ->getForm();

        $form->handleRequest($request);
        $croppedImage = null;
        $croppedThumbnail = null;
        if ($form->isSubmitted()) {
            // faking an error to let the page re-render with the cropped images
            $form->addError(new FormError('🤩'));
            $croppedImage = \sprintf('data:image/jpeg;base64,%s', base64_encode($crop->getCroppedImage()));
            $croppedThumbnail = \sprintf('data:image/jpeg;base64,%s', base64_encode($crop->getCroppedThumbnail(200, 150)));
        }

        return $this->render('ux_packages/cropperjs.html.twig', [
            'package' => $package,
            'form' => $form,
            'croppedImage' => $croppedImage,
            'croppedThumbnail' => $croppedThumbnail,
        ]);
    }
}
{% extends 'base.html.twig' %}

{% block body %}
    {% if croppedImage is null %}
        {{ form_start(form) }}
        <div class="row">
            <div class="col-9">{{ form_widget(form) }}</div>
            <div class="col-3">
                <div id="cropper-preview" style="overflow: hidden;width: 200px;height: 200px;"></div>
            </div>
        </div>
            <button type="submit" class="btn btn-dark mt-5" style="width: 100%;">Crop it!</button>
        {{ form_end(form) }}
    {% else %}
        {# show the cropped image after submit! #}
        <div class="row">
            <div class="col-9">
                <figure class="figure">
                    <img src="{{ croppedImage }}" class="figure-img img-fluid rounded" alt="Cropped image">
                    <figcaption class="figure-caption">The cropped image.</figcaption>
                </figure>
            </div>
            <div class="col-3">
                <figure class="figure">
                    <img src="{{ croppedThumbnail }}" class="figure-img img-fluid rounded" alt="Cropped thumbnail">
                    <figcaption class="figure-caption">A thumbnail of the cropped image.</figcaption>
                </figure>
            </div>
        </div>
    {% endif %}
{% endblock %}
Symfony logo

UX Cropper.js

Install It

$ composer require symfony/ux-cropperjs