There are two popular packages when for serialization when working with Symfony or with PHP in general. One serializes null as undefined and one as null.

The one serializing it as undefined by is the JMS serializer. Both packages are getting faster and depending on when you look at them one of them is slightly in the lead. As the Symfony package has the backwards compatibility promise and receives more updates, I switched from JMS serializer to the Symfony serializer. Which serializes null as null by default.

Luckily this is only the default. I use a helper function to serialize basically all my responses. When serializing I use the SKIP_NULL_VALUES option to serialize null as undefined so the frontend components don't need to change anything. Additionally it seems to be best practice to use undefined in the frontend for optional values.

protected function getSerializedResponse($data = null): JsonResponse
{
    $serializer = $this->get('serializer');

    if (null === $data) {
        return new JsonResponse('', JsonResponse::HTTP_NO_CONTENT, [], true);
    }

    $serializedData = $serializer->serialize($data, 'json', [
        ObjectNormalizer::SKIP_NULL_VALUES => true,
    ]);

    return new JsonResponse($serializedData, 200, [], true);
}