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-04-25中級

Rork アプリでスケルトンスクリーンを実装する — 読み込み中の体感速度を改善する実践パターン

Rork で作ったアプリの「読み込み中」が、ただのぐるぐるスピナーで終わっていませんか。スケルトンスクリーンと Shimmer エフェクトを取り入れるだけで、体感速度と離脱率は大きく変わります。実装の型と注意点を整理しました。

Rork515UI/UX3React Native209Skeleton Screenパフォーマンス30

リリースしたばかりの Rork アプリをユーザーテストに出したとき、一番よくもらうフィードバックが「起動したあとの数秒が、なんとなく不安」という感想です。処理自体はきちんと動いているのに、真っ白な画面の真ん中でスピナーが回っているだけだと、ユーザーには「止まっているのでは?」「通信がおかしいのでは?」と映ってしまうのです。

この「体感の待ち時間」を短く見せる定番のテクニックがスケルトンスクリーンです。実測の読み込み時間は変わらないのに、離脱率が目に見えて下がる — そんな効果を出してくれる、地味ですが実用性の高い UI パターンです。ここではRork で作ったアプリ(React Native / Expo)に無理なく組み込める実装パターンを、私が実際に使い分けている形でまとめます。

なぜ ActivityIndicator だけでは足りないのか

Rork が自動生成するコードには、デフォルトで ActivityIndicator(いわゆるスピナー)が入っていることが多いです。これは何もないよりは遥かに良いのですが、次のような場面では体感が悪くなります。

  • ヒーロー画像が大きい画面(ホームやプロフィール)
  • リスト表示で 10 件以上のセルが同時に現れる画面
  • タブ切り替えや画面遷移の直後

スピナーは「処理中」以上の情報を伝えません。一方、スケルトンスクリーンは「あとここに画像が出て、ここにタイトルが入るんだな」というこれから現れる情報の予告を兼ねています。Facebook や YouTube が初期ロードをスケルトンで見せているのは、これが理由です。

私は Rork で案件を作るとき、1 秒以上待つ可能性がある画面は全部スケルトンに置き換えるようにしています。ユーザーテストの体感で、明らかに「サクサク感」が変わるからです。

もっとも小さなスケルトンを書いてみる

最小構成を見てもらうのが早いので、まずは静的なスケルトンから始めます。グレーのブロックを並べるだけです。

import { View, StyleSheet } from 'react-native';
 
// プレースホルダーの最小単位
export function SkeletonBlock({ width, height, radius = 8 }: {
  width: number | string;
  height: number;
  radius?: number;
}) {
  return (
    <View
      style={[
        styles.block,
        { width, height, borderRadius: radius },
      ]}
    />
  );
}
 
const styles = StyleSheet.create({
  block: {
    backgroundColor: '#e5e7eb', // Tailwind でいう gray-200 相当
  },
});

これを使ってプロフィールカードのスケルトンを組み立てると、次のようになります。

import { View, StyleSheet } from 'react-native';
import { SkeletonBlock } from './SkeletonBlock';
 
export function ProfileSkeleton() {
  return (
    <View style={styles.card}>
      <SkeletonBlock width={64} height={64} radius={32} />
      <View style={styles.body}>
        <SkeletonBlock width="70%" height={16} />
        <View style={{ height: 8 }} />
        <SkeletonBlock width="40%" height={12} />
      </View>
    </View>
  );
}
 
const styles = StyleSheet.create({
  card: { flexDirection: 'row', padding: 16, alignItems: 'center' },
  body: { flex: 1, marginLeft: 12 },
});

この段階で、真っ白な画面が「コンテンツのレイアウト予告」に変わります。動きがなくても、スピナーよりは確実にマシです。まずはここまで入れるだけでも、体感は変わります。

Shimmer エフェクトで「生きている感」を足す

静的なスケルトンに満足できなくなったら、次のステップは Shimmer(ハイライトが左から右に流れるアニメーション)です。React Native Reanimated 3 を使うと、JS スレッドに負担をかけずに実装できます。

import { useEffect } from 'react';
import { StyleSheet, View } from 'react-native';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withRepeat,
  withTiming,
  Easing,
} from 'react-native-reanimated';
import { LinearGradient } from 'expo-linear-gradient';
 
// 流れるハイライト付きスケルトン
export function ShimmerBlock({ width, height, radius = 8 }: {
  width: number;
  height: number;
  radius?: number;
}) {
  const translateX = useSharedValue(-width);
 
  useEffect(() => {
    // -width 〜 +width を 1.2 秒でループ
    translateX.value = withRepeat(
      withTiming(width, { duration: 1200, easing: Easing.linear }),
      -1,
      false,
    );
  }, [width]);
 
  const style = useAnimatedStyle(() => ({
    transform: [{ translateX: translateX.value }],
  }));
 
  return (
    <View style={[styles.container, { width, height, borderRadius: radius }]}>
      <Animated.View style={[StyleSheet.absoluteFill, style]}>
        <LinearGradient
          colors={['transparent', 'rgba(255,255,255,0.6)', 'transparent']}
          start={{ x: 0, y: 0 }}
          end={{ x: 1, y: 0 }}
          style={StyleSheet.absoluteFill}
        />
      </Animated.View>
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: {
    backgroundColor: '#e5e7eb',
    overflow: 'hidden', // 内側のグラデーションを角丸で切り抜く
  },
});

ポイントは 3 つです。useSharedValuewithRepeat でアニメーションを UI スレッドに逃がすこと、LinearGradient の両端を transparent にして光が滑らかに抜けること、overflow: 'hidden' で角丸の内側にハイライトを閉じ込めること。この辺りを外すと、角がチラついたり、カクついた動きになったりします。

Reanimated の詳しい使い方はRork Reanimated 3 とジェスチャーハンドラーで作る高度なアニメーションで別途まとめています。Shimmer 以外のマイクロインタラクションを足したくなったら、合わせて読んでみてください。

リストのスケルトンは「実際に出す件数」を意識する

一覧画面のスケルトンを作るとき、私が最初の頃によくやっていた失敗が、スケルトンを 3 件しか出さないことでした。画面に 7 件見えるリストで 3 件分しかプレースホルダーを出さないと、実データに切り替わった瞬間に画面が一気に下に伸びて、ユーザーの視線が迷子になります。

実装の指針として、以下を意識すると安定します。

  • 画面に見える最大件数(目安: flatListHeight / itemHeight より少し多め)をスケルトンで出す
  • セルの高さは、実データと厳密に揃える
  • リストのトップから連続して並べ、間隔も本物と同じにする
import { FlatList, View } from 'react-native';
import { SkeletonBlock } from './SkeletonBlock';
 
const SKELETON_ITEMS = Array.from({ length: 8 }); // 画面に収まる最大件数 + 余裕
 
export function ListSkeleton() {
  return (
    <FlatList
      data={SKELETON_ITEMS}
      keyExtractor={(_, i) => `sk-${i}`}
      renderItem={() => (
        <View style={{ padding: 16, flexDirection: 'row', alignItems: 'center' }}>
          <SkeletonBlock width={48} height={48} radius={24} />
          <View style={{ flex: 1, marginLeft: 12 }}>
            <SkeletonBlock width="80%" height={14} />
            <View style={{ height: 6 }} />
            <SkeletonBlock width="50%" height={12} />
          </View>
        </View>
      )}
      scrollEnabled={false}
    />
  );
}

scrollEnabled={false} にしているのは、読み込み中にユーザーが誤ってスクロールすると、実データ到着時の描画コストが跳ね上がるからです。この 1 行だけでも、古めの Android 実機で明らかに差が出ます。

切り替えのタイミングで気を付けていること

スケルトンから実データへの切り替えは、油断するとちらつきます。私がよく入れている対策は次の 2 つです。

1. 最低表示時間を設ける

読み込みが速すぎる場合にスケルトンが一瞬だけ出てしまうと、逆に「画面が点滅した」ように見えます。最低 200 〜 300 ミリ秒は出す、という下駄を履かせることで、安定感が出ます。

import { useEffect, useState } from 'react';
 
export function useDelayedReady(ready: boolean, minMs = 250) {
  const [show, setShow] = useState(ready);
  useEffect(() => {
    if (!ready) {
      setShow(false);
      return;
    }
    const id = setTimeout(() => setShow(true), minMs);
    return () => clearTimeout(id);
  }, [ready, minMs]);
  return show;
}

実際の呼び出しはこんな形です。

const { data, isLoading } = useProfile();
const ready = useDelayedReady(!isLoading);
 
return ready ? <Profile data={data} /> : <ProfileSkeleton />;

2. フェードイン 1 枚分の余裕を入れる

スケルトンを消して実データを出すとき、Animated.View で 150 ms ほどのフェードインを挟むと、切り替えの境目が柔らかくなります。バッテリーや描画コストはほぼ増えないので、試す価値はあります。

つまずきやすいポイント

最後に、Rork 案件のレビューで何度も指摘したことがある落とし穴を挙げておきます。

  • 画像領域だけスケルトンを忘れる: テキストはプレースホルダー化したのに、画像プレースホルダーだけ抜けていて、画像が後から「バン」と挿し込まれて画面が揺れるケース。Image の親に width / height をしっかり固定し、そのサイズでスケルトンを出してください。
  • Shimmer が重すぎる: Shimmer を画面上で 50 個以上同時に動かすと、古い Android で体感フレームレートが落ちます。リストでは表示中のセルだけアニメーションを動かすようにすれば、だいたい解決します。
  • スケルトンにブランドカラーを使う: 良かれと思ってブランドカラーの薄い色でスケルトンを塗ると、本物のコンテンツと紛らわしくなります。ニュートラルなグレー(ダークモードでは #2a2a2a 付近)にとどめたほうが、読みやすさは上です。

関連して、ネットワーク不安定時の UI 設計についてはRork アプリの不安定なネットワーク下での UX とエラーステート設計でも整理しています。スケルトンとエラーステートは表裏一体のテーマなので、あわせて見ていただけると全体の完成度が上がります。

次にやってみてほしいこと

いきなり全画面を作り替える必要はありません。まずは起動直後のホーム画面データを多く扱うリスト画面の 2 つだけ、スケルトンに置き換えてみてください。ユーザーテストで「待ち時間が気にならなくなった」と言われるはずです。その手応えを掴んでから、他の画面に広げていくのがおすすめです。

もう一歩 UI/UX の体系を整えたい方は、Rork アプリの UX デザインパターン完全ガイドで全体像をまとめていますので、そちらもどうぞ。読んでくださってありがとうございます。あなたのアプリが、ほんの少しでも気持ちよく動くようになれば嬉しいです。

シェア

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

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

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

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

関連記事

開発ツール2026-03-30
Rork × React Native Reanimated & Gesture Handler 実践ガイド — 60fps を維持するアニメーション設計と高度なジェスチャーインタラクション
Reanimated 3 と Gesture Handler で 60fps を保つ設計を、壁紙・癒し系アプリの実運用で踏んだ落とし穴とともに整理しました。runOnJS の発火遅延、useDerivedValue キャッシュ、FlashList との併用、cancelAnimation 漏れなど、公式ドキュメント外の知見を実測値付きで共有します。
開発ツール2026-07-10
React Compiler を Expo に入れて、手書き memo を 41 箇所消しました
Rork が生成した React Native 画面に React Compiler を導入し、再レンダリング回数を Profiler で実測しました。手書き memo/useCallback の削除判断、効かない箇所の見分け方、CI での回帰検知までをまとめています。
開発ツール2026-06-29
New Architecture へ移行したら本番だけ画面がカクつくとき — interop 層への静かなフォールバックを計測して切り分ける運用メモ
New Architecture へ切り替えた Rork アプリが、開発中は快適なのにリリースビルドの実機でだけ一覧スクロールがカクつく。原因は旧式ネイティブモジュールが interop 層へ静かにフォールバックしていたことでした。検知の計測と段階的な切り戻しの実務メモです。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →