---
title: First Screen Render Optimization
docType: default
---

# First Screen Render Optimization

After code injection completes, the miniapp enters the first screen render phase: `Page.onLoad` fires, the component tree initializes, initial data is transferred to the view layer, the view layer renders, and the process ends when `Page.onReady` fires.

Time cost in this phase comes from two directions: **rendering complexity** (node count, data size) and **data wait time** (network requests, local reads). Both affect when users see usable content.

## Slim Down Initial Data

Data in `data` is transferred from the logic layer to the view layer before each render. The size of the initial `data` directly affects transfer time and render time.

- **Unused first-screen data**: Data for pagination, tab content, or dialogs that aren't shown on the first screen should not be in `data` initially — they will still participate in data transfer and render computation. Set them via `setData` only when needed.
- **Non-render data in data**: Intermediate computation results, config objects, and static lookup tables that have nothing to do with the view should not be in `data`. Store them on `this` as regular properties instead.

## Reduce First-Screen Node Count

More nodes on the page means more layout computation and drawing overhead in the view layer.

**Common strategies:**

- **Conditional rendering with delay**: Off-screen areas (collapsed content, bottom sections) can be rendered after the first screen completes
- **Paginated list rendering**: Only render the first few items on the first screen; append more as the user scrolls
- **Defer non-essential component mounts**: Too many custom components increase initialization overhead; components not needed on the first screen can be mounted lazily

## Use Local Cache

For first-screen data that doesn't change frequently, use a "render from cache first, update in background" strategy. The miniapp provides [setStorage](/en/miniapp/develop/ray/api/storage/setStorage) and [getStorage](/en/miniapp/develop/ray/api/storage/getStorage) for local caching; local data returns much faster than network requests.

1. Write to local cache after a successful request
2. On next startup, render from cache first
3. Simultaneously fetch fresh data in the background
4. Update the view when data returns

```tsx
import { getStorage, setStorage } from '@ray-js/ray';

function HomePage() {
  const [data, setData] = useState(null);

  useEffect(() => {
    // Render from cache first
    getStorage({
      key: 'panelData',
      success: (res) => setData(res.data),
    });

    // Fetch fresh data in background
    fetchPanelData().then((fresh) => {
      setData(fresh);
      setStorage({ key: 'panelData', data: fresh });
    });
  }, []);
}
```

## Use Skeleton Screen

Even with caching, some data still requires waiting. A skeleton screen displays the rough layout of the page before data arrives, letting users perceive that the page is loading rather than unresponsive, reducing perceived wait time.

See [Skeleton Screen](/en/miniapp/develop/ray/guide/optimization/experience/skeleton).

## Miniapp Preloading

For miniapps opened via cards or shortcut entries, call [preDownloadMiniApp](/en/miniapp/develop/ray/api/base/container/preDownloadMiniApp#predownloadminiapp) and [preloadPanel](/en/miniapp/develop/ray/api/base/panel/preloadPanel#preloadpanel) to pre-download the package in the background. When the user taps, the download phase is skipped and the flow goes directly to injection, noticeably improving first-open experience.
Default home page cards have built-in pre-download logic — no additional configuration needed.

**Best for**: Home page cards that launch a miniapp on tap. Default cards pre-download miniapp assets so users go directly to launch on tap.

## Use Memory Cache

When a miniapp is re-entered shortly after closing, the base library memory cache mechanism can skip download and some injection steps, speeding up re-entry. This is enabled by default; don't disable it unless there is a specific reason.

See [Memory Cache](/en/miniapp/develop/ray/guide/ability/memoryCache).

## Checklist

| Symptom | Possible Cause | Optimization |
| ------- | -------------- | ------------ |
| Long first-screen render time | Initial data includes non-first-screen fields; too much data transferred | Keep only fields needed for first-screen render; load other data on demand or lazily |
| Slow layout calculation, rendering lag | Too many nodes on first screen | Use conditional rendering or paginated rendering for collapsed areas and lists; only render visible content on first screen |
| First-screen content waits for network | Every startup depends on network response before rendering | For infrequently changing data, render from local cache first, then update view with background request |
| Long perceived blank screen | No content shown during data loading | Add skeleton screen to display page outline before data arrives |
| Slow first open after card tap | Package download only starts when tapped | Call `preDownloadMiniApp` / `preloadPanel` to pre-download in background |
| Noticeably slower second open | Memory cache has been disabled | Confirm memory cache is not disabled; restore default configuration |
