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-01中級

VirtualizedList を ScrollView の中に置くと出る警告 — Rork で正しく直すパターン集

Rork のアプリで FlatList を ScrollView に入れたら出る『VirtualizedLists should never be nested』警告。原因と、ListHeaderComponent や FlashList を使った正しい解決パターンを実例で解説します。

Rork515React Native209FlatList9ScrollViewトラブルシューティング77FlashList5

Rork で「ヘッダーがあって、その下に長い記事リストがある」ような画面を作っていると、ある日突然コンソールに次のような赤い警告が表示されます。

VirtualizedLists should never be nested inside plain ScrollViews
with the same orientation because it can break windowing and other
functionality - use another VirtualizedList-backed container instead.

私自身、Rork で複数のアプリを開発していて、この警告に何度も遭遇しました。動作自体はしてしまうので「とりあえず無視」と片付けがちですが、放置するとリストが長くなった瞬間にスクロールがガタつき、メモリ消費が一気に膨らみます。今回は Rork から生成された React Native プロジェクトで、この警告を「正しく」解消するためのパターンを整理しておきます。

なぜこの警告が出るのか

FlatListSectionList の正体は VirtualizedList です。これは「画面に映っている分だけレンダリングし、見えなくなった行は破棄する」仕組みで、長いリストでも軽快に動くように設計されています。

ところが、これを ScrollView の中に同じ縦方向で入れてしまうと、外側の ScrollView が中の全要素を一度に展開して測ろうとします。すると FlatList の仮想化が完全に無効化され、1万件のデータを全部メモリに展開するといった事態が起きます。警告はそれを防ぐためのものです。

私はこれを「2つの巻き取り装置を直列につなぐと、結局どちらも巻き取れなくなる」というイメージで理解しています。スクロールを担当するコンテナは画面に1つだけ、というのが基本です。

解決策1: FlatList の Header / Footer に寄せる(最有力)

一番おすすめなのが、ScrollView をやめて FlatList 1枚で構成し直す方法です。固定したいヘッダーは ListHeaderComponent に、フッターは ListFooterComponent に渡します。

import { FlatList, View, Text, Image, StyleSheet } from "react-native";
 
type Article = { id: string; title: string; excerpt: string };
 
export default function FeedScreen({ articles }: { articles: Article[] }) {
  // ヘッダーに描きたかった内容を関数として渡す
  // → これで ScrollView をネストせずに済む
  const Header = (
    <View style={styles.header}>
      <Image source={require("./assets/banner.png")} style={styles.banner} />
      <Text style={styles.heading}>新着の記事</Text>
    </View>
  );
 
  return (
    <FlatList
      data={articles}
      keyExtractor={(item) => item.id}
      ListHeaderComponent={Header}
      ListFooterComponent={<View style={{ height: 32 }} />}
      renderItem={({ item }) => (
        <View style={styles.row}>
          <Text style={styles.title}>{item.title}</Text>
          <Text style={styles.excerpt} numberOfLines={2}>
            {item.excerpt}
          </Text>
        </View>
      )}
    />
  );
}
 
const styles = StyleSheet.create({
  header: { padding: 16, gap: 12 },
  banner: { width: "100%", height: 140, borderRadius: 12 },
  heading: { fontSize: 20, fontWeight: "700" },
  row: { padding: 16, borderBottomWidth: 1, borderBottomColor: "#eee" },
  title: { fontSize: 16, fontWeight: "600", marginBottom: 4 },
  excerpt: { color: "#555" },
});

警告が消えるだけでなく、ヘッダーやフッターも仮想化の恩恵を受けられるので、リストが長くなってもスクロールが滑らかなままです。Rork の AI に修正を依頼するときも、「ScrollView を外して FlatList の ListHeaderComponent にまとめてください」と具体的に指示するときれいに直してくれます。

解決策2: 横方向のリストならネスト OK

警告文をよく読むと「同じ向きの場合(with the same orientation)」と書かれています。つまり、外側が縦の ScrollView、内側が横スクロールの FlatListhorizontaltrue にしたもの)であればネストして問題ありません。

<ScrollView>
  <Text style={styles.section}>おすすめ特集</Text>
 
  <FlatList
    data={featured}
    horizontal
    showsHorizontalScrollIndicator={false}
    keyExtractor={(item) => item.id}
    renderItem={({ item }) => <FeatureCard item={item} />}
  />
 
  <Text style={styles.section}>新着</Text>
  {/* ↓ こちらも縦の FlatList にしたいなら ListHeaderComponent に寄せる */}
</ScrollView>

ホーム画面でよくある「カテゴリ別の横カルーセルが縦に並ぶ」レイアウトは、この組み合わせで素直に書けます。

解決策3: SectionList でセクション分割する

「ヘッダー → セクションA → セクションB」のように、構造的にグループ分けされたリストなら SectionList が自然です。複数のセクションをまとめつつ、内部は仮想化されたままになります。

import { SectionList, Text, View } from "react-native";
 
const sections = [
  { title: "今週のピックアップ", data: weeklyArticles },
  { title: "新着", data: latestArticles },
];
 
<SectionList
  sections={sections}
  keyExtractor={(item, index) => item.id + index}
  renderSectionHeader={({ section: { title } }) => (
    <Text style={styles.sectionTitle}>{title}</Text>
  )}
  renderItem={({ item }) => <ArticleRow item={item} />}
  ListHeaderComponent={<HomeBanner />}
  stickySectionHeadersEnabled
/>

stickySectionHeadersEnabled を有効にすると、見出しがスクロール中に上に貼り付くので、コンテンツ量が多いアプリでも現在地が分かりやすくなります。

解決策4: 大量データなら FlashList に置き換える

リストの件数が数百〜数千を超えてくる場合、Shopify が公開している @shopify/flash-list への置き換えを検討してください。FlatList と API がほぼ同じなのに、内部実装はセル再利用が効いていて、スクロール時のフレーム落ちが大幅に減ります。

import { FlashList } from "@shopify/flash-list";
 
<FlashList
  data={articles}
  estimatedItemSize={88}        // 行の平均高さを必ず指定する
  keyExtractor={(item) => item.id}
  ListHeaderComponent={<HomeHeader />}
  renderItem={({ item }) => <ArticleRow item={item} />}
/>

estimatedItemSize の指定を忘れると本来の性能が出ないので注意してください。Rork から生成した直後だと FlatList で実装されることが多いため、データ量が増えてきたタイミングで FlashList へ切り替える、という運用が私の中では定着しています。

やってはいけない回避策

警告を消したい一心で、つい ScrollViewnestedScrollEnabled を有効にしたり、FlatListscrollEnabledfalse にして「全件レンダリングさせる」方向で逃げてしまうケースがあります。これは警告が消えるだけで根本解決にはなりません。

特に scrollEnabled={false} + 親 ScrollView の組み合わせは、データ件数が増えた瞬間にメモリ不足でアプリがクラッシュする原因になります。私も初期に一度やらかして、TestFlight で配布した後に低スペック端末で連続クラッシュ、という事故を起こしました。回避策ではなく、上の4つのうちどれかに必ず寄せるのが安全です。

もうひとつ、よく見るアンチパターンが「リストを <View style={{ flex: 1, height: 800 }}> で囲んで高さを固定する」やり方です。これは警告が消えたように見えるだけで、実際は仮想化が壊れたまま、強制的に高さの上限を与えているだけです。明示的なピクセル高さでリストを囲みたくなったら、それは解決策1へ立ち戻るサインだと考えてください。

もうひとつだけ補足しておくと、React Navigation を使っている場合、画面のコンテナ自体がすでに flex: 1View です。スクロールさせたいからといって、その上に ScrollView を被せる必要はありません。FlatList を画面直下に置くだけで、ちゃんとスクロールします。Rork の AI が ScrollView を勝手に足してくる場面で、そのまま受け入れずに「外しても動くか」を一度疑ってみるのがおすすめです。

見落としがちな2つのケース

このルールが理解できていても、つまずきやすいパターンが2つあります。

ひとつは KeyboardAvoidingView の中に ScrollView、その中に FlatList という組み合わせです。検索フォームの下に候補リストを出す画面でよく出来上がる構造です。ここでも基本は同じで、外側の ScrollView を外して、フォーム部分を FlatListListHeaderComponent に寄せます。KeyboardAvoidingViewFlatList と直接組み合わせても問題なく動きます。

もうひとつは Modal の中で ScrollViewFlatList を包んでいる ケース。モーダルは別世界のように感じますが、内部に対しても同じ警告が適用されます。モーダルの中身もひとつの画面と捉えて、解決策1を適用してください。

外側の ScrollView を外したのに警告が消えない場合、自作の <Screen><Container> といったラッパーコンポーネントの中に ScrollView が潜んでいることがあります。私はこういうとき、疑わしいコンポーネントを <View style={{ flex: 1, borderWidth: 4, borderColor: "red" }}> で包んで、画面上で「どれがリストを包んでいるか」を視覚的に確認しています。

どのパターンを選ぶか — 私の判断軸

実際の開発で迷ったら、こんな順番で考えています。

  • リストが「画面のメイン要素」なら → 解決策1(FlatList + ListHeaderComponent)
  • 「縦の中に横スクロールが混ざる」なら → 解決策2(horizontal FlatList をネスト)
  • セクションごとに区切りたいなら → 解決策3(SectionList)
  • 件数が大きい/パフォーマンスが悩みなら → 解決策4(FlashList)

迷ったらまず解決策1を試してみてください。多くのケースで、ScrollView を外すだけでコードがすっきりして、警告も自然に消えます。

React Native 全体のコンポーネント設計や仮想化の考え方をもう少し体系的に学びたい方には、Rork で生成されたコードを読み解く React Native 基礎ガイド が参考になるかもしれません。フォームや入力で似たような「キーボードに隠れる」問題に困っている場合は、Rork でキーボードと入力欄が重なる問題の修正ガイド も合わせて読むと、スクロール周りの設計判断が一段深く理解できます。

まずは1画面だけ直してみてください

VirtualizedList の警告は、見つけた瞬間に全画面を一気に直そうとすると挫折しがちです。私自身は、警告がいちばん多く出ている画面を1つだけ選び、そこを FlatList + ListHeaderComponent に寄せ替えるところから始めるようにしています。

低スペック端末でも滑らかなスクロールを体感できると、「あぁ、これが警告が言いたかったことか」と一気に腹落ちします。Rork でこの先もっと長いリストを扱うアプリを作るときに、確実に効いてくる土台になります。

シェア

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

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

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

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

関連記事

開発ツール2026-05-22
FlatList の onEndReached が連続発火して API を叩きすぎる問題の原因と対処
FlatList の onEndReached が画面遷移時や初期描画時に何度も呼ばれて、ページネーション API を多重に叩いてしまう問題。Rork で生成したリスト画面でも頻発するこの挙動の原因と、実運用で使える対処パターンを整理しました。
開発ツール2026-05-02
FlatList でカクつき始めたら読む — Rork アプリを FlashList v2 で快適にする実装手順
Rork で生成したアプリの長いリストが重くなったとき、FlashList v2 へ移行することでスクロールが大幅に滑らかになります。estimatedItemSize が不要になった v2 の思想を踏まえた、現実的な移行手順を解説します。
開発ツール2026-04-22
FlatList が空白で何も映らない—Rork アプリのリスト崩れを切り分ける
Rork で生成したアプリの FlatList が真っ白で何も表示されないときに、どこから疑うべきかを順番にまとめました。data・flex・keyExtractor・親要素の高さを見直すだけで直ることも多いです。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →