TGViewer
SuperOleg dev notes SuperOleg dev notes @super_oleg_dev · 1.75K subscribers
Post #63 1.22K
Учитывая уже развернутый сервис imgproxy, я решил делать Image компонент поверх этого сервиса - это закрыло вопросы лишней нагрузки на SSR приложения, оптимизации под разные форматы, отдачи нужного формата в рантайме без тега picture (что позволяет сделать очень простую разметку).

Еще не решенная проблема этого выбора - невозможность использовать сервис при разработке, для изображений на localhost.

.avif формат у нас был отключен из-за высокой нагрузки при генерации изображений в этом формате, было интересно разобраться в проблеме, даже помогли немножко улучшить сервис - https://github.com/imgproxy/imgproxy/issues/938.
Оказалось, что для достижения сравнимого качества изображения, для AVIF надо указывать значительно более низкий уровень качества при оптимизации, хорошее сравнение форматов можно найти тут - https://www.industrialempathy.com/posts/avif-webp-quality-settings/

В итоге, после точечной настройки, мы подключили AVIF, и в среднем, с примерно одинаковым качеством, это снизило размер итоговых изображений на 30-50% по сравнению с webP.

Следующий кейс - автоматическое сохранение пропорций, вычисление aspect-ratio.
На примере некста, логику получения размера картинки можно встроить в сборку - https://github.com/vercel/next.js/blob/canary/packages/next/build/webpack/loaders/next-image-loader.js.
В tramvai я добавил простой лоадер, который переиспользует логику file-loader (компромисс, т.к. по хорошему надо использовать Asset Modules), но при именованном импорте отдает объект с данными об изображении, которые необходимы для сохранения пропорций:

jsx
import { image } from './image.png';

const { src, width, height } = image;

<img src={src} width={width} height={height} />

По аналогии с next/image, добавил в компонент несколько вариантов верстки (и без необходимости поддерживать старые браузеры и наличии aspect-ratio, верстка невероятно простая):

- адаптивная, но не больше размера изображения (intrinsic)
- резиновая под размер контейнера (responsive)
- фиксированный размер (fixed)
- заполняющая доступное пространство (fill)

Если для intrinsic и fixed изображений достаточно учесть только плотность пикселей на экране, и передать в srcset 1x и 2x изображения, то для оптимальной responsive и fill верстки srcset должен содержать изображения под совершенно разные размеры экранов.

По этой причине, отдельной задачей было исследовать аналитику посещения наший приложений на tinkoff.ru для создания минимального но в то же время полного списка экранов, которые мы будем учитывать в srcset при генерации изображений.

Пример размеров из этого списка:

- 720px - популярная ширина мобилок 360px плюс 2x плотность пикселей
- 1366px - популярная ширина на десктопе
- 3840px - еще одна популярная ширина на десктопе, 1920px плюс 2x плотность пикселей

Таким образом, пользователи большинства устройств получат изображение минимально необходимого размера и оптимального качества.

Еще из интересного, простые и удобные абстракции:

- Свойство quality - позволяет при необходимости сконфигурировать оптимизацию через imgproxy более высокого качества, или наоборот более низкого
- Свойство priority - по умолчанию делаем все изображения c loading="lazy", но можем повысить приоритет и убрать этот атрибут, а с самым высоким приоритетом автоматически добавляем preload link на страницу для этой картинки (тривиальная задача для SSR фреймворка), что идеально подходит для Largest Contentful Paint изображения

Это был очень интересный опыт, и интересно как дальше будет развиваться новый Image компонент.

Например, в будущем я планирую отвязать его от imgproxy, и перенести в список публичных пакетов, которые публикуются в open-source.

Также, надо будет выбрать хороший способ создания blur плейсхолдера для изображений.
GitHub Error: assignment to entry in nil map · Issue #938 · imgproxy/imgproxy Hello! imgproxy@3.7.0, latest Docker image. Try to use format_quality:jpeg:70:avif:45:webp:70 preset on images, and imgproxy crashed with stacktrace: panic: assignment to entry in nil map goroutine...
  • 👍 7
More from @super_oleg_dev
  1. Sep 10, 2026Посмотреть на работу анимаций и скролла можно в нашей демке, основанной на примере Remix ф…
  2. Sep 10, 2026Далее, про скролл. В принципе для SPA-приложений это база, если при переходах на разные ст…
  3. Sep 10, 2026Раз уж меня прорвало, вспомню ещё один интересный кейс. Решали примерно в одно время две п…
  4. Sep 10, 2026Вместо тысячи слов, пример работы и сразу разметка, которая долетает в HTML - и наш механи…
  5. Sep 10, 2026Когда-то давно рассказывал про интеграцию стриминга и deferred загрузки данных в Tramvai -…
  6. Sep 7, 2026TIL, небольшой, но интересный кейс! Делал кастомный анализ бандла через Claude, пишет мне…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →