自分のためにインスタのフィードの並びを確認できるツールを作った

Tech

PWAアプリを30分で開発

インスタのフィード投稿の並び順を事前にシミュレーションできるWebアプリを作りました。

画像をアップロードし、ドラッグ&ドロップで並び替えながら、実際のインスタのフィードと同じ3列グリッド(4:5比率)でプレビューできます。

🔗 Feed Preview

以下はgithubリポジトリのREADMEからの引用ですが、以下のような機能と技術スタックです。

お気に入りポイントはvite-plugin-pwaでPWA化した点です。

主な機能

  • 画像アップロード: 複数画像を一括選択してアップロード
  • 3列グリッド表示: Instagram同様の4:5比率・3px間隔のグリッドレイアウト
  • ドラッグ&ドロップ: 画像をドラッグして自由に並び替え(挿入位置に応じて後続画像が自動シフト)
  • 画像削除: 各画像のホバー時に表示される削除ボタンで個別削除
  • ダーク/ライトモード: ヘッダーのトグルボタンで切り替え(設定はlocalStorageに保存)
  • PWA対応: ホーム画面に追加してネイティブアプリのように使用可能

画像の保存・管理について

  • サーバー・データベースは使用していない。すべての処理はブラウザ上で完結する
  • 画像データはサーバーに送信されず、外部に一切アップロードされない
  • アップロードされた画像は IndexedDB にBlobとしてブラウザ内に永続保存される
  • 画像の並び順は localStorage に保存される
  • ページをリロードしても画像と並び順は保持される
  • 画像削除時はIndexedDBからも削除され、URL.revokeObjectURL() でメモリを解放する
  • ブラウザのサイトデータを消去すると画像データも削除される

技術スタック

  • Vite + React + TypeScript
  • Tailwind CSS
  • @dnd-kit(ドラッグ&ドロップ)
  • vite-plugin-pwa(PWA対応)

なぜ作ったか

単純に自分が使いたかったからです。

似たようなアプリは複数存在していますが、投稿データを取得するInstagram APIの仕様が変わったのかどれも有料になってしまいました。

コラージュアプリで代用していましたがどうにも使いづらいので、自分が欲しい機能だけのシンプルなアプリとして公開しました。

Claude Codeで30分くらいで完成しました。

スマホ対応で発生した問題まとめ

スマホ対応で直面した問題がいくつかあったので紹介します。

1. @dnd-kit PointerSensorとTouchSensorの競合

ドラッグ&ドロップ機能はdnd-kitを使用して実装しました。React向けの軽量なライブラリです。
https://dndkit.com/
PointerSensorはdnd-kitの標準センサーで、Pointer Events APIを使用してマウス・タッチ・ペンなど全てを処理する仕様ですが、TouchSensorと併用するとタッチイベントを両方が捕捉しようとして競合が発生していました。

  • 対策:
    • PointerSensor → MouseSensorに変更
    • マウスはMouseSensor、タッチはTouchSensorが専任で処理するように分離

2. Safari / PWAでドラッグが動作しない

Safariはデフォルトのタッチジェスチャー(スクロール等)を優先し、TouchSensorがタッチイベントを捕捉する前にブラウザが横取りするという事象がありました。
touch-action: manipulationの設定のみでは不十分でした。

  • 対策:
    • ドラッグ対象要素にtouch-action: noneを設定
      • dnd-kit公式ではTouchSensor使用時はmanipulationを推奨しているが、Safariで確実に動作させるため、より厳格なnoneを採用
        • これによりブラウザのデフォルトタッチ処理を完全に無効化し、JSのイベントハンドラが確実にタッチを受け取れるように

3. Safari 3D Touch(Haptic Touch)との競合

Safariで画像を長押しすると、ドラッグより先にOSの3D Touch/Haptic Touchメニュー(画像保存・コピー等)が発動してしまいます。

  • 対策:
    • -webkit-touch-callout: none:Safari固有のコールアウトメニューを無効化
    • onContextMenu preventDefault:コンテキストメニューイベント自体を抑制
    • pointer-events-none(img要素):画像要素がタッチイベントの対象にならないようにし、親divのドラッグハンドラが確実に発火するようにした

感想

フィードの見栄えを気にしている人はもう少数派かもしれない。。