Cradova Documentation

Learn how to build amazing web applications with Cradova

Cradova Performance

This guide covers performance optimization in Cradova including the List class and virtualized rendering.

Why Performance Matters

Cradova uses direct DOM manipulation without virtual DOM diffing. This is generally faster, but large lists can still impact performance. Use these techniques to optimize.

List Class

For large arrays, use the List class which provides efficient rendering:

import { List, p, div } from "cradova";

// Create list with render function
const itemsList = new List(
  ["Item 1", "Item 2", "Item 3"],
  (item, index) => p(item, { id: "item-" + index })
);

// Render in component
const MyComponent = function(ctx: Comp) {
  return div(itemsList.Element);
};

// Add item
itemsList.push("New Item");

// Remove item
itemsList.splice(0, 1);

// Update item
itemsList[0] = "Updated";

List with Object Data

interface Todo {
  id: number;
  text: string;
  done: boolean;
}

const todos = new List<Todo>(
  [
    { id: 1, text: "Learn Cradova", done: false },
    { id: 2, text: "Build app", done: true }
  ],
  (todo, index) => div(
    { className: todo.done ? "done" : "" },
    p(todo.text),
    button(todo.done ? "Undo" : "Complete", {
      onclick: () => {
        const newTodos = [...todos.data];
        newTodos[index] = { ...todo, done: !todo.done };
        todos.data = newTodos;
      }
    })
  )
);

List Options

const list = new List(data, renderFn, {
  itemHeight: 50,      // Fixed height for virtualization
  containerHeight: 400 // Container height
});

Virtualized List

For very large lists (1000+ items), use virtualized-list:

import { List, virtualizedList } from "cradova";

const bigList = new List(
  Array(10000).fill(0).map((_, i) => `Item ${i}`),
  (item, index) => div(p(item)),
  { 
    itemHeight: 40,
    containerHeight: 600,
    virtualized: true
  }
);

useMemo for Expensive Calculations

const ExpensiveComponent = function(ctx: Comp) {
  const [items, setItems] = ctx.useState([]);
  
  // Only recalculate when items change
  const sorted = ctx.useMemo(() => {
    return items
      .filter(i => i.active)
      .sort((a, b) => a.name.localeCompare(b.name))
      .map(i => ({ ...i, displayName: i.name.toUpperCase() }));
  }, [items]);
  
  // Expensive computation not needed on every render
  const total = ctx.useMemo(() => 
    items.reduce((sum, i) => sum + i.price, 0),
  [items]);
  
  return div(
    h1("Total: {{ content }}quot; + total),
    sorted.map(item => p(item.displayName))
  );
};

useCallback for Stable References

const Parent = function(ctx: Comp) {
  const [count, setCount] = ctx.useState(0);
  
  // Stable reference - won't cause child re-renders
  const handleClick = ctx.useCallback(() => {
    setCount(c => c + 1);
  }, []);
  
  return div(
    h1("Count: " + count),
    // Child only re-renders when count changes, not on every render
    ChildComponent({ onClick: handleClick })
  );
};

Avoiding Unnecessary Re-renders

// ❌ WRONG - new function every render
const BadComponent = function(ctx: Comp) {
  return button("Click", {
    onclick: () => console.log("clicked") // New function each render
  });
};

// ✅ CORRECT - stable function
const GoodComponent = function(ctx: Comp) {
  const handleClick = ctx.useCallback(() => {
    console.log("clicked");
  }, []);
  
  return button("Click", { onclick: handleClick });
};

Signal vs useState

Use Signal for global/shared state, useState for component-local:

// Global state - use Signal
const AppStore = new Signal({ user: null, theme: "light" });

// Component state - use useState
const MyComponent = function(ctx: Comp) {
  const [local, setLocal] = ctx.useState("");
  
  // Reading global state (triggers when it changes)
  const theme = AppStore.data.theme;
  
  return div();
};

Optimizing Large Lists

1. Pagination

const PaginatedList = function(ctx: Comp) {
  const [items, setItems] = ctx.useState(allItems);
  const [page, setPage] = ctx.useState(0);
  const pageSize = 20;
  
  const currentPage = ctx.useMemo(() => {
    const start = page * pageSize;
    return items.slice(start, start + pageSize);
  }, [items, page]);
  
  return div(
    currentPage.map(item => p(item.name)),
    div(
      button("Prev", { disabled: page === 0, onclick: () => setPage(p => p - 1) }),
      button("Next", { disabled: (page + 1) * pageSize >= items.length, onclick: () => setPage(p => p + 1) })
    )
  );
};

2. Infinite Scroll

const InfiniteList = function(ctx: Comp) {
  const [items, setItems] = ctx.useState(initialItems);
  const [loading, setLoading] = ctx.useState(false);
  
  ctx.useEffect(() => {
    const handleScroll = () => {
      if (window.scrollY + window.innerHeight >= document.body.offsetHeight - 100) {
        loadMore();
      }
    };
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);
  
  const loadMore = async () => {
    if (loading) return;
    setLoading(true);
    const more = await fetchMore();
    setItems([...items, ...more]);
    setLoading(false);
  };
  
  return div(
    items.map(item => p(item.name)),
    loading ? p("Loading...") : null
  );
};

3. Window Virtualization

For extremely long lists (10k+ items), only render visible items:

interface Todo {
  id: number;
  text: string;
  done: boolean;
}

const todos = new List<Todo>(
  [
    { id: 1, text: "Learn Cradova", done: false },
    { id: 2, text: "Build app", done: true }
  ],
  (todo, index) => div(
    { className: todo.done ? "done" : "" },
    p(todo.text),
    button(todo.done ? "Undo" : "Complete", {
      onclick: () => {
        const newTodos = [...todos.data];
        newTodos[index] = { ...todo, done: !todo.done };
        todos.data = newTodos;
      }
    })
  )
);
```0

## Performance Checklist

- [ ] Use `useMemo` for expensive calculations
- [ ] Use `useCallback` for stable function references
- [ ] Use List class for large arrays (100+ items)
- [ ] Consider virtualization for very large lists (1000+)
- [ ] Implement pagination for large datasets
- [ ] Use lazy loading for routes
- [ ] Prefer Signal for global state over prop drilling
- [ ] Keep components small and focused

## Bundle Size

Cradova's framework is ~15KB minified. For comparison:

- React + ReactDOM: ~45KB
- Vue: ~35KB
- Cradova: ~15KB

This makes Cradova suitable for:
- Mobile applications
- Low-bandwidth environments
- Quick initial load requirements

Get Started in Minutes

Simple Setup

Follow our clear documentation to get your first project running in no time.

Write Less Code

Focus on your application's logic, not boilerplate.

Join the Discord

Get support and share your creations with a growing community.

Join Discord