<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>candiesocean • RSS feed</title>
    <link>https://candiesocean.com</link>
    <description>Candiesocean is a programming blog focusing on mobile app development</description>
    <language>en-gb</language>
    <lastBuildDate>Wed, 11 Feb 2026 10:22:13 GMT</lastBuildDate>
    <atom:link href="https://candiesocean.com/feed.xml" rel="self" type="application/rss+xml"/>
    
    <item>
      <title>Uniwind</title>
      <link>https://candiesocean.com/blog/uniwind</link>
      <guid isPermaLink="true">https://candiesocean.com/blog/uniwind</guid>
      <pubDate>Wed, 19 Nov 2025 04:29:00 GMT</pubDate>
      <description></description>
      <content:encoded><![CDATA[<p>What if you could style your <strong>React Native</strong> app with Tailwind&#39;s <code>className</code> syntax, get dramatically better <strong>performance</strong> than existing class-based libraries, and still stay much closer to the performance you expect from well-optimized styling systems— while keeping the developer experience you already love from Tailwind on the web?</p>
<p>This is exactly what <strong>Uniwind</strong> is about. It takes Tailwind&#39;s utility-first workflow, integrates deeply with <strong>Metro</strong> at build time, and delivers performance far beyond what earlier Tailwind for React Native solutions achieved. By shifting as much work as possible out of runtime and into the build step, Uniwind keeps your app fast while letting you write clean, expressive Tailwind classes.</p>
<p>Today I&#39;m going to break down what Uniwind really is, how it works, and why it might be one of the most forward-thinking styling solutions in the React Native ecosystem.</p>
<YouTubeEmbed videoId="r-NWPIGWI1k" title="Uniwind - Tailwind for React Native" />

<h2>How React Native Styling Works</h2>
<p><img src="https://github.com/user-attachments/assets/762b70d6-ddcd-4f81-a8a8-83a9edfdd2e2" alt="Uniwind Benchmarks"></p>
<p>To understand Uniwind, we need to start with how React Native applies styles.</p>
<p>Every style follows the same path: it starts in your <code>JavaScript</code> code, passes through <code>Fabric</code>, goes into the <strong>C++ core</strong>, and finally reaches the native UI layer—<code>UIKit</code> on iOS and <code>Views</code> on Android. There&#39;s no way around this. Even the built-in <code>StyleSheet</code> travels through the same flow, which is why it remains the baseline for raw speed.</p>
<p><img src="https://github.com/user-attachments/assets/c47b3d65-49bf-43ea-841f-b0eb7dd46b7e" alt="Styling approach"></p>
<p>The challenge is that traditional Tailwind-style libraries add extra work -</p>
<ul>
<li><input checked="" disabled="" type="checkbox"> parsing <code>className</code> strings</li>
<li><input checked="" disabled="" type="checkbox"> mapping utilities to style props</li>
<li><input checked="" disabled="" type="checkbox"> resolving variants dynamically</li>
<li><input checked="" disabled="" type="checkbox"> handling theme logic in JavaScript</li>
</ul>
<p>All of this happens at <strong>runtime</strong>, which is why older className libraries are slower compared to <code>StyleSheet</code>-based approaches.</p>
<p>So the real question became: <em>How do we bring Tailwind&#39;s developer experience to React Native without adding heavy runtime overhead?</em></p>
<blockquote>
<p>Uniwind is the answer to that question.</p>
</blockquote>
<h2>What is Uniwind?</h2>
<p>Uniwind is a <strong>Tailwind-first styling system</strong> for React Native built on three core ideas:</p>
<h3>1. Build-time Tailwind parsing</h3>
<p>Uniwind uses Tailwind&#39;s new <strong>CSS engine</strong>, where design tokens and utilities live in a <code>global.css</code> file. Tailwind scanning and utility generation happen during the Metro build, <em>not while your app is running</em>. Uniwind precomputes static style objects before the bundle even loads on the device, eliminating most of the runtime work that slowed down previous solutions.</p>
<blockquote>
<p><em>Edit (Nov 20, 2025): Jacek Pudysz, the author of Uniwind, pointed out that there is still a small amount of runtime computation . So while the heavy lifting shifts to build time, expect a minimal runtime layer to stay in place.</em></p>
</blockquote>
<p><img src="https://github.com/user-attachments/assets/60e9f90f-e16a-4426-a91a-d51ee7583f2d" alt="Uniwind Benchmarks"></p>
<h3>2. A custom CSS parser for React Native</h3>
<p>Alongside Tailwind utilities, you can write regular CSS for reusable patterns or semantic classes. Uniwind merges Tailwind utilities and your custom CSS at build time so everything behaves consistently in React Native.</p>
<h3>3. Deep Metro integration</h3>
<p>By wrapping your Metro config, Uniwind instructs Metro to:</p>
<ul>
<li><input checked="" disabled="" type="checkbox"> load and parse your CSS</li>
<li><input checked="" disabled="" type="checkbox"> run Tailwind&#39;s engine</li>
<li><input checked="" disabled="" type="checkbox"> generate TypeScript types</li>
<li><input checked="" disabled="" type="checkbox"> embed optimized style objects directly into the bundle</li>
</ul>
<pre><code class="language-ts">const { getDefaultConfig } = require(&#39;expo/metro-config&#39;)
const { withUniwindConfig } = require(&#39;uniwind/metro&#39;)

const config = getDefaultConfig(__dirname)

const uniwindConfig = withUniwindConfig(config, {
  [INFO] relative path to your global.css file
  cssEntryFile: &#39;./src/global.css&#39;,
  dtsFile: &#39;./src/uniwind-types.d.ts&#39;,
  extraThemes: [
    &#39;og&#39;,
    &#39;blue&#39;,
    &#39;navy&#39;,
    &#39;red&#39;,
    &#39;orange&#39;,
    &#39;purple&#39;,
    &#39;rosy&#39;,
    &#39;grey&#39;,
    &#39;candy&#39;,
  ],
})

module.exports = uniwindConfig
</code></pre>
<h2>Features That Make Uniwind Special</h2>
<p>What sets Uniwind apart is how naturally it brings the full Tailwind workflow into React Native while staying focused on performance.</p>
<h3>Platform-specific styling</h3>
<p>You can target <strong>iOS</strong> and <strong>Android</strong> directly inside your <code>className</code> strings using Tailwind-style platform selectors like <code>ios:</code> and <code>android:</code> — without writing <code>Platform.select</code> or conditional logic.</p>
<pre><code class="language-tsx">&lt;View className=&quot;ios:bg-red-500 android:bg-blue-500&quot; /&gt;
</code></pre>
<h3>CSS variable-based theming</h3>
<p>The theming system is powered entirely by <strong>CSS variables</strong> through Tailwind&#39;s new <code>@theme</code> directive. You define your design tokens once—colors, backgrounds, borders, surface layers—and then use semantic classes like <code>bg-background</code>, <code>text-foreground</code>, <code>bg-card</code>, or <code>text-muted</code> throughout your app. There&#39;s <em>no JavaScript theme provider</em> and <em>no context</em>. Themes like <code>light</code>, <code>dark</code>, or custom ones such as <code>ocean</code> or <code>peach</code> all work by simply switching CSS variables.</p>
<pre><code class="language-css">@import &#39;tailwindcss&#39;;
@import &#39;uniwind&#39;;

@layer theme {
  :root {
    @variant light {
      --color-background: #e7edde;
      --color-foreground: #111827;
      --color-lime: #e8ede1;
      --color-primary: #234823;
      --color-lemon: #ffce47;
      --color-neon: #e0ffc2;
      --color-neonlite: #c4dea9;
      --color-neonlite-200: #739265;
      --color-overlay-strong: #ffffff33;
      --color-overlay-light: #ffffff1a;
      --color-card: #fde6da;
      --color-card-strong: #f3cfc2;
      --color-chip: #ffce47;
      --color-chip-text: #234823;
      --color-cta: #234823;
      --color-cta-text: #e7edde;
      --color-card-border: #23482314;
    }

    @variant dark {
      --color-background: #0f1a11;
      --color-foreground: #f7f9f4;
      --color-lime: #1c2a1d;
      --color-primary: #9fe870;
      --color-lemon: #ffd25f;
      --color-neon: #264023;
      --color-neonlite: #1b3219;
      --color-neonlite-200: #66bb62;
      --color-overlay-strong: #00000088;
      --color-overlay-light: #00000044;
      --color-card: #182418;
      --color-card-strong: #101a11;
      --color-chip: #ffd25f;
      --color-chip-text: #0f1a11;
      --color-cta: #9fe870;
      --color-cta-text: #0f1a11;
      --color-card-border: #ffffff1f;
    }

    @variant og {
      --color-background: #f9f6ee;
      --color-foreground: #1f1b16;
      --color-lime: #f5efe0;
      --color-primary: #4c2f18;
      --color-lemon: #ffb347;
      --color-neon: #ffe8cc;
      --color-neonlite: #fdd8c1;
      --color-neonlite-200: #c68a65;
      --color-overlay-strong: #00000029;
      --color-overlay-light: #00000014;
      --color-card: #fff7ef;
      --color-card-strong: #fbd2ae;
      --color-chip: #ffb347;
      --color-chip-text: #4c2f18;
      --color-cta: #4c2f18;
      --color-cta-text: #fff7ef;
      --color-card-border: #4c2f1814;
    }

    @variant blue {
      --color-background: #e6f0ff;
      --color-foreground: #0b1a2b;
      --color-lime: #d6e8ff;
      --color-primary: #1e3a8a;
      --color-lemon: #60a5fa;
      --color-neon: #dbeafe;
      --color-neonlite: #bfdbfe;
      --color-neonlite-200: #3b82f6;
      --color-overlay-strong: #0b1a2b26;
      --color-overlay-light: #0b1a2b14;
      --color-card: #eef4ff;
      --color-card-strong: #c7d7ff;
      --color-chip: #60a5fa;
      --color-chip-text: #0b1a2b;
      --color-cta: #1e3a8a;
      --color-cta-text: #e6f0ff;
      --color-card-border: #1e3a8a1a;
    }

    @variant red {
      --color-background: #fff1f0;
      --color-foreground: #3b0606;
      --color-lime: #ffe1de;
      --color-primary: #a10d2b;
      --color-lemon: #ff8f70;
      --color-neon: #ffd7cf;
      --color-neonlite: #ffb3a7;
      --color-neonlite-200: #e04f54;
      --color-overlay-strong: #00000029;
      --color-overlay-light: #00000012;
      --color-card: #ffe6e1;
      --color-card-strong: #ffbfb2;
      --color-chip: #ff8f70;
      --color-chip-text: #5a0a15;
      --color-cta: #a10d2b;
      --color-cta-text: #fff6f4;
      --color-card-border: #a10d2b1a;
    }

    @variant orange {
      --color-background: #fff5eb;
      --color-foreground: #271201;
      --color-lime: #ffe8d2;
      --color-primary: #d45500;
      --color-lemon: #ffb347;
      --color-neon: #ffd8b5;
      --color-neonlite: #ffc494;
      --color-neonlite-200: #ff8a00;
      --color-overlay-strong: #00000029;
      --color-overlay-light: #00000012;
      --color-card: #fff0e0;
      --color-card-strong: #ffcf9f;
      --color-chip: #ffb347;
      --color-chip-text: #482100;
      --color-cta: #d45500;
      --color-cta-text: #fff5eb;
      --color-card-border: #d455001a;
    }

    @variant purple {
      --color-background: #f8f3ff;
      --color-foreground: #1f1033;
      --color-lime: #ede1ff;
      --color-primary: #6b21a8;
      --color-lemon: #c084fc;
      --color-neon: #ead7ff;
      --color-neonlite: #d8b4fe;
      --color-neonlite-200: #a855f7;
      --color-overlay-strong: #1f103326;
      --color-overlay-light: #1f103312;
      --color-card: #f0e4ff;
      --color-card-strong: #d8b4fe;
      --color-chip: #c084fc;
      --color-chip-text: #32104f;
      --color-cta: #6b21a8;
      --color-cta-text: #f8f3ff;
      --color-card-border: #6b21a81a;
    }

    @variant rosy {
      --color-background: #fff5f7;
      --color-foreground: #311018;
      --color-lime: #ffe9ef;
      --color-primary: #be185d;
      --color-lemon: #f472b6;
      --color-neon: #ffd6e9;
      --color-neonlite: #fbcfe8;
      --color-neonlite-200: #db2777;
      --color-overlay-strong: #31101826;
      --color-overlay-light: #31101812;
      --color-card: #ffeef3;
      --color-card-strong: #fbc0d8;
      --color-chip: #f472b6;
      --color-chip-text: #4f0f2b;
      --color-cta: #be185d;
      --color-cta-text: #fff5f7;
      --color-card-border: #be185d1a;
    }

    @variant candy {
      --color-background: #fff0fb;
      --color-foreground: #301433;
      --color-lime: #ffe0f4;
      --color-primary: #d946ef;
      --color-lemon: #facc15;
      --color-neon: #ffd6f6;
      --color-neonlite: #f5a9e9;
      --color-neonlite-200: #f472d0;
      --color-overlay-strong: #30143329;
      --color-overlay-light: #30143312;
      --color-card: #ffe8f8;
      --color-card-strong: #ffb7eb;
      --color-chip: #facc15;
      --color-chip-text: #5d1a58;
      --color-cta: #d946ef;
      --color-cta-text: #fff0fb;
      --color-card-border: #d946ef1a;
    }
  }
}
</code></pre>
<h3>Native component colors</h3>
<p>Uniwind also handles platform accents and native component colors. Tailwind color utilities map to native properties like cursor color, selection color, and more. You write a Tailwind color class, and Uniwind routes it to the correct native prop automatically.</p>
<h3>Full TypeScript support</h3>
<p>On the developer experience side, Uniwind generates a complete <code>uniwind-types.d.ts</code> file during the build, giving you autocomplete for all utilities, platform variants, theme tokens, and your custom CSS classes.</p>
<h3></h3>
<p>Uniwind brings Tailwind’s <code>className</code> workflow to React Native with <strong>near–StyleSheet performance</strong> by moving all heavy work—utility parsing, CSS processing, theming, and type generation—to <strong>build time</strong> through deep Metro integration. It supports platform-specific styling, CSS-variable theming, native color mapping, and full TypeScript types, offering a fast, modern, Tailwind-first styling system for React Native.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Liquid Glass in React Native</title>
      <link>https://candiesocean.com/blog/liquid-glass-in-react-native</link>
      <guid isPermaLink="true">https://candiesocean.com/blog/liquid-glass-in-react-native</guid>
      <pubDate>Sat, 18 Oct 2025 00:44:11 GMT</pubDate>
      <description></description>
      <content:encoded><![CDATA[<p>Apple unveiled their biggest design overhaul in over a decade - <strong>Liquid Glass</strong> - a design language that makes every UI element look like actual frosted glass that reacts to light and movement in real-time. And while we&#39;re waiting for iOS 26 to drop later this year, I&#39;m going to show you EXACTLY how to implement these stunning glass effects in your React Native apps(beta).</p>
<p>Apple describes Liquid Glass as a dynamic material that combines the optical properties of glass with a sense of fluidity, offering glass-like transparency that adapts in real time. Designed to replicate the optical qualities of glass, it uses Gaussian blur algorithms combined with realistic lighting shaders to create authentic glass appearances, while dynamically refracting and reflecting any element placed behind it to achieve a lifelike effect, it adjusts transparency levels and contrast to ensure text and icons remain readable regardless of background content, maintaining a clear hierarchy between content and controls.</p>
<h2>Liquid Glass in React Native using expo-liquid-glass-view</h2>
<p>Fundamentally, the <strong>Liquid Glass</strong> glass component works just like any other React Native view.You wrap your content inside <code>&lt;ExpoLiquidGlassView /&gt;</code> and provide additional prop of type, tint, cornerRadius, cornerStyle.</p>
<ul>
<li><input checked="" disabled="" type="checkbox"> Type</li>
<li><input checked="" disabled="" type="checkbox"> Tint</li>
<li><input checked="" disabled="" type="checkbox"> Corner Radius</li>
<li><input checked="" disabled="" type="checkbox"> Corner Style</li>
</ul>
<br/>

<figure>
<Image
  src="https://github.com/user-attachments/assets/b6437d8e-fd22-460d-9e6b-7440dfba2eb0"
  width={800}
  height={480}
  alt=""
/>

</figure>

<h2>Installation &amp; Setup</h2>
<br/>

<ol>
<li>Add the package</li>
</ol>
<pre><code class="language-bash">npx expo install expo-liquid-glass-view
</code></pre>
<ol start="2">
<li>Install CocoaPods
cd ios &amp;&amp; pod install</li>
</ol>
<pre><code class="language-bash">cd ios &amp;&amp; pod install
</code></pre>
<ol start="3">
<li>Prebuild the iOS project and run your app</li>
</ol>
<pre><code class="language-bash">npx expo prebuild --platform ios
</code></pre>
<p>To use the component</p>
<pre><code class="language-tsx">import {
  CornerStyle,
  ExpoLiquidGlassView,
  LiquidGlassType,
} from &#39;expo-liquid-glass-view&#39;
</code></pre>
<pre><code class="language-tsx">&lt;ExpoLiquidGlassView
  style={style}
  cornerRadius={16}
  cornerStyle={CornerStyle.Continuous}
  type={LiquidGlassType.Regular}
&gt;
  {children}
&lt;/ExpoLiquidGlassView&gt;
</code></pre>
<p>Liquid Glass design language unveils Apple&#39;s new direction, bringing nature-inspired glass, fluidity, and depth to the forefront of user interfaces. As the mobile design landscape continues to evolve toward more immersive and dynamic experiences, experimenting with Liquid Glass effects now can help position your apps ahead of the curve in modern UI design.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>FlashList v2</title>
      <link>https://candiesocean.com/blog/flashlist-v2</link>
      <guid isPermaLink="true">https://candiesocean.com/blog/flashlist-v2</guid>
      <pubDate>Sat, 23 Aug 2025 09:41:06 GMT</pubDate>
      <description></description>
      <content:encoded><![CDATA[<h2>FlashList v2: The List Component That Just Works</h2>
<p>FlashList was released in 2022 as a simple goal - make lists that don&#39;t glitch or show blank cells. It worked decent and grew to over 2 million monthly downloads. But the team at Shopify knew they could do better. The old version required developers to estimate item sizes, had platform-specific native code, and sometimes struggled with precision when scrolling to specific items. So when React Native introduced its New Architecture with synchronous layout measurements, they saw an opportunity to solve these problems once and for all.</p>
<br/>

<div className="flex space-x-4">

<p><img src="https://github.com/user-attachments/assets/4610e52a-79ed-461a-8fd1-b8e8f8f0ba09" alt="UI"></p>
<p><img src="https://github.com/user-attachments/assets/98897358-80b2-4add-9740-fa62759f4875" alt="UI"></p>
</div>

<h2>Idea</h2>
<p>React Native&#39;s New Architecture lets you measure layouts synchronously instead of waiting for async bridge calls. This means FlashList v2 can measure items, calculate perfect positions, and make corrections before anything renders on screen (useLayoutEffect). FlashList doesn&#39;t ask for any estimates anymore. The list figures out exactly what it needs and renders it perfectly, every single time.Even LegendList in it&#39;s latest beta version has made estimatedItemSize an optional prop as its using average sizes, whether you have simple text items or complex cards of different heights, FlashList handles it automatically.</p>
<br/>

<p><img src="https://github.com/user-attachments/assets/99d04b7b-ac8e-4b45-9b28-f87bbc08eb10" alt="New Architecture"></p>
<h2>Features</h2>
<p>Now we&#39;ve pixel-perfect scrolling to any item - scrollToIndex and scrollToItem are much more precise than before. Support for complex layouts like Pinterest-style masonry grids, now with just one inline <code>masonry</code> prop, and you can even use overrideItemLayout with masonry for extra control. Horizontal lists are much improved - items can resize within the lists and FlashList no longer renders an extra item just to measure list height. Plus better memory management and faster load times. And it maintains your scroll position. Since there&#39;s no native code anymore, it works consistently across iOS, Android, and web.</p>
<h2>Usage</h2>
<p>To get started, we&#39;ll install the library in a brand new project</p>
<pre><code class="language-bash">npx expo install @shopify/flash-list
</code></pre>
<p>We&#39;re using Tanstack Query and will get all the items using <code>useFeed</code> hook which is internally using <code>useInfiniteQueries</code></p>
<pre><code class="language-ts">const {
  items,
  status,
  refetch,
  isFetchingNextPage,
  isRefetching,
  hasNextPage,
  fetchNextPage,
} = useFeed({ enabled: true, pageSize: PAGE_SIZE })
</code></pre>
<p>We&#39;ll define our <code>renderItem</code> with different item types</p>
<ol>
<li>giveaway</li>
<li>template</li>
<li>photo</li>
<li>event(default)</li>
</ol>
<pre><code class="language-ts">const renderItem = useCallback(({ item }: { item: FeedItem }) =&gt; {
  if (item.type === &#39;giveaway&#39;) {
    return &lt;GiveawayCard item={item} /&gt;
  }
  if (item.type === &#39;template&#39;) {
    return &lt;TemplateCard item={item} /&gt;
  }
  if (item.type === &#39;photo&#39;) {
    return &lt;PhotoCard item={item} /&gt;
  }
  return &lt;EventCard event={item} /&gt;
}, [])
</code></pre>
<p>We&#39;ve <code>getItemType</code> and <code>keyExtractor</code> (optional)</p>
<pre><code class="language-ts">const getItemType = useCallback((item: FeedItem) =&gt; item.type, [])
const keyExtractor = useCallback((item: FeedItem) =&gt; item.id, [])
</code></pre>
<pre><code class="language-ts">&lt;FlashList
  data={items}
  renderItem={renderItem}
  keyExtractor={keyExtractor}
  getItemType={getItemType}
  showsVerticalScrollIndicator={false}
  onEndReachedThreshold={0.5}
  ListFooterComponent={ListFooterComponent}
  refreshControl={
    &lt;RefreshControl
      onRefresh={refetch}
      refreshing={isRefetching}
      colors={[COLORS.PRIMARY]}
      tintColor={COLORS.PRIMARY}
    /&gt;
  }
/&gt;
</code></pre>
<p>To refetch the data when we are halfway through we will introduce onEndReached
that will fetchNextPage when we have more data available</p>
<pre><code class="language-ts">const onEndReached = useCallback(() =&gt; {
  if (hasNextPage &amp;&amp; !isFetchingNextPage) {
    fetchNextPage()
  }
}, [hasNextPage, isFetchingNextPage, fetchNextPage])
</code></pre>
<h2>Conclusion</h2>
<p>FlashList v2 sets a new standard for list rendering in React Native—solving a challenge that developers have faced for years. From buttery-smooth scrolling and masonry grid layouts to optimized memory usage and cross-platform consistency, it delivers performance and flexibility where it matters most.</p>
<p>These improvements highlight the strength of the new architecture and the thoughtful work of the team behind it. Since most apps rely heavily on lists, trying out FlashList v2 is a decent choice.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>