RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/アプリ開発
アプリ開発/2026-05-11中級

壁紙アプリの画像が遅かった:Rork で expo-image に切り替えて分かったこと

RorkのデフォルトImageコンポーネントからexpo-imageに切り替えて壁紙アプリの画像読み込みを高速化した実体験。blur hashプレースホルダー実装まで解説します。

expo-image5壁紙アプリ28パフォーマンス30React Native209画像最適化

2014年頃から個人でアプリ開発を続けてきた中で、壁紙アプリというジャンルをずっと軸の一つに置いてきました。累計5,000万ダウンロードを超えるまでの道のりで、ユーザーが最初に感じる「重さ」ほどアンインストール率に直結するものはないと実感しています。

Rork であるプロトタイプを作っていたとき、スクロールのたびに画像が白くなる現象が気になり始めました。実機で動かすと、高解像度の壁紙画像が表示されるまでに400〜600ms かかっていて、スクロールするたびに白いチラつきが起きていました。Rork が生成したコードを確認すると、標準の <Image> コンポーネントが使われていました。これが問題の根本です。

なぜ Rork の標準 Image コンポーネントは遅くなるのか

React Native の標準 <Image> コンポーネントは基本的な表示には問題ありませんが、画像の多いアプリでは顕著な制約があります。

  • メモリキャッシュが弱い: 一度表示した画像でも、スクロールで画面外に出ると再読み込みが発生することがあります
  • Progressive loading がない: 画像全体がロードされるまで何も表示されません
  • プレースホルダーを自前で実装する必要がある: ロード中の見た目を工夫しようとすると、かなりの実装コストがかかります

Rork は React Native の標準 API を使ってコードを生成するため、特に指定しない限り <Image> が使われます。壁紙アプリのように高解像度の画像を数十枚スクロールさせるケースでは、この設計は体験の足を引っ張ります。

個人開発でAdMobを軸に収益化しているアプリほど、ユーザーが最初の数秒で離れるかどうかが広告表示回数に直結します。画像の表示速度はサイレントキラーです。

expo-image が解決すること

expo-image は Expo チームが開発した画像コンポーネントで、React Native 標準の <Image> の上位互換として設計されています。

主な特徴は次の通りです。

  • LRU ディスクキャッシュ: 一度ダウンロードした画像はディスクに保存され、次回はほぼ瞬時に表示されます
  • blur hash サポート: 画像の代替として色のぼかし(blur hash)をプレースホルダーとして表示できます。画像が読み込まれる前から「それらしい色」が見えるため、体感速度が大きく改善します
  • Crossfade アニメーション: 画像のロード完了時になめらかにフェードインします
  • プログレッシブ JPEG 対応: 段階的に画像を表示できます

壁紙アプリに限らず、画像を多用するアプリなら expo-image への切り替えはほぼ確実に効果があります。

Rork で expo-image を使うよう指示するプロンプト

新規プロジェクトを始める場合、プロンプトに明示的に記載するだけで expo-image を使ったコードが生成されます。

画像の表示にはすべて expo-image の Image コンポーネントを使ってください。
React Native の標準 Image は使用しないでください。
cachePolicy="memory-disk" を設定し、
placeholder={{ blurhash: "..." }} も追加してください。

既存プロジェクトを修正する場合は、次のように伝えます。

現在 <Image source={{ uri: ... }} /> で実装されている部分を、
expo-image の <Image> に置き換えてください。
cachePolicy="memory-disk" を設定し、
placeholder={{ blurhash: "..." }} も追加してください。

Rork がコードを生成したあと、package.jsonexpo-image が依存関係として追加されていることを確認してください。

実装コード:標準 Image から expo-image への切り替え

Before(Rork が生成するデフォルトのコード):

import { Image } from 'react-native';
 
// 壁紙一覧の各アイテム
function WallpaperItem({ uri }: { uri: string }) {
  return (
    <Image
      source={{ uri }}
      style={{ width: '100%', height: 200 }}
      resizeMode="cover"
    />
  );
}

After(expo-image に切り替え後):

import { Image } from 'expo-image';
 
// blur hash は画像アップロード時にサーバー側で事前生成しておきます
// このサンプルはウォームグレー系のプレースホルダー
const FALLBACK_BLURHASH = 'L6PZfSi_.AyE_3t7t7R**0o#DgR4';
 
function WallpaperItem({ uri, blurhash }: { uri: string; blurhash?: string }) {
  return (
    <Image
      source={{ uri }}
      style={{ width: '100%', height: 200 }}
      contentFit="cover"          // resizeMode="cover" と同じ意味
      placeholder={{ blurhash: blurhash ?? FALLBACK_BLURHASH }}
      transition={200}            // 200ms のフェードイン
      cachePolicy="memory-disk"   // メモリ + ディスクにキャッシュ
    />
  );
}

contentFitresizeMode に相当します。"cover""cover""contain""contain" と対応しているため、値の書き換えは不要です。

API 面での変更はほぼこれだけです。大半は find-and-replace と少量の追記で済むため、既存アプリへの移行は1〜2時間で完了することが多いです。

blur hash の生成方法

blur hash は画像の「色の要約」をエンコードした短い文字列です。expo-image はこの文字列をデコードして、ぼかした色のプレビューをリアルタイムで描画します。

壁紙アプリの場合、画像は管理者側がアップロードするケースが多いため、アップロード時に blur hash を生成してデータベースに保存しておくのが現実的です。

Node.js でのサーバーサイド生成例:

import { encode } from 'blurhash';
import sharp from 'sharp';
 
async function generateBlurhash(imagePath: string): Promise<string> {
  const { data, info } = await sharp(imagePath)
    .raw()
    .ensureAlpha()
    .resize(32, 32, { fit: 'inside' }) // 処理を軽くするため小さくリサイズ
    .toBuffer({ resolveWithObject: true });
 
  return encode(
    new Uint8ClampedArray(data),
    info.width,
    info.height,
    4, // X コンポーネント数
    4  // Y コンポーネント数
  );
}

Cloudflare R2 に壁紙を保存している場合は、Workers でアップロードを受け取るタイミングに blur hash 生成を組み込んで、結果を D1 や KV に保存しておくのが効率的です。

blur hash が用意できない段階では、アプリ全体で共通のフォールバック blur hash を定数として定義しておくことで、「全画像が白いまま」という状態は避けられます。

切り替え後の変化:実機での確認

expo-image に切り替えたあと、Rork Companion アプリで実機テストしたところ、次のような変化がありました。

  • 初回表示: blur hash のプレースホルダーが即時表示されるため、体感的な待機感がほぼなくなりました
  • 2回目以降: ディスクキャッシュにより、同じ画像の再表示がほぼ瞬時になりました
  • スクロールバック時: 画面外に出た画像もメモリキャッシュに残るため、スクロールバックしたときのチラつきがなくなりました

ベンチマークツールを使った数値計測ではなく実感の話になりますが、個人開発で壁紙アプリを長く運用してきた感覚として、ユーザーレビューの「重い」「遅い」という声が減るのはこうした細かい改善の積み重ねだと考えています。

アーティストとして、見た目の美しさを追求しても、それを届ける体験が遅ければ意味がありません。ユーザーが壁紙を一枚見つける瞬間まで、技術的な「摩擦」を限りなく小さくしておくことが、私がアプリ開発で大切にしていることの一つです。

関連する記事

画像最適化以外の全体的なパフォーマンス改善に取り組む場合は、Rork アプリ パフォーマンス最適化完全ガイドも参考になります。

大量アイテムのリスト表示が重い場合は、FlatList でカクつき始めたら読む記事をあわせてご覧ください。

壁紙アプリを最初から作りたい方は、RorkでウォールペーパーアプリをApp Storeに公開した話が参考になります。


まず1つの <Image> コンポーネントだけ expo-image に切り替えて、実機で blur hash のプレースホルダーが出ることを確認してみてください。体感の変化が分かれば、残りの移行は自然に進みます。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

アプリ開発2026-07-14
壁紙を長押しして、プレビューと保存メニューを出す — Rork の Expo アプリで context menu を組む
壁紙ギャラリーのカードを長押ししても何も起きない。iOS利用者はプレビュー付きのメニューを期待します。Pressableだけでは届かない理由から、zeegoでのネイティブcontext menu実装、スクロールとの競合解消、保存・共有アクション、Android向けの自前フォールバックまで、動くコードでまとめます。
アプリ開発2026-06-27
画面を離れてもアニメーションが回り続ける — Rork(Expo)アプリのフォーカス連動でバッテリーを守る
Rork が生成した Expo アプリで、別画面に進んだりホームに戻ったあともグラデーションや呼吸アニメーションが裏で回り続け、ジャンクとして表に出ないままバッテリーを静かに削っていく——その原因は Reanimated の withRepeat が UI スレッドで走り続けることと、ナビゲーションスタックが画面をマウントしたまま保持することにあります。useIsFocused と AppState を束ねたライフサイクル設計で、画面外とバックグラウンドのループを確実に止める実装を、動くコードで示します。
アプリ開発2026-06-23
壁紙アプリの色がくすむ問題と Display P3 — 広色域を配信パイプラインで詰める
同じ壁紙が実機ではくすんで見える。原因は広色域(Display P3)とsRGBの取り違えでした。6本の壁紙アプリで色域を詰めた実装手順に加え、画素が本当に広色域かを見分ける方法、配信前ゲートのスクリプト、Android広色域までまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →