RORK LABJP
MAX — Rork Max builds native Swift apps instead of React Native, targeting the whole Apple ecosystemDEVICES — From one description it spans iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessageNATIVE — It unlocks AR and LiDAR scanning, 3D games with Metal, Home Screen widgets, Dynamic Island, and on-device Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, joined by Peak XV and a16z SpeedrunPAPERLINE — Rork acquired the app builder Paperline and plans to stay acquisitive for engineering talentMARKET — With 743,000+ monthly visits, and Gartner expects 75% of new apps to be low-code or no-code by end of 2026MAX — Rork Max builds native Swift apps instead of React Native, targeting the whole Apple ecosystemDEVICES — From one description it spans iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessageNATIVE — It unlocks AR and LiDAR scanning, 3D games with Metal, Home Screen widgets, Dynamic Island, and on-device Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, joined by Peak XV and a16z SpeedrunPAPERLINE — Rork acquired the app builder Paperline and plans to stay acquisitive for engineering talentMARKET — With 743,000+ monthly visits, and Gartner expects 75% of new apps to be low-code or no-code by end of 2026
Articles/Dev Tools
Dev Tools/2026-07-15Advanced

The Comma That Fell to the Start of a Line: Rork, Quote Cards, and Japanese Line Breaking

React Native's Text component does not guarantee Japanese line-breaking rules. Here are the violation rates I measured, a WORD JOINER implementation that survives copy and VoiceOver, an onTextLayout audit harness, and how Rork Max (SwiftUI) differs.

Rork510React Native209Expo143JapaneseTypographyText

Premium Article

I picked up the iPhone SE and stopped halfway through the screen.

A quote card in one of my wallpaper apps. The second line began with a Japanese comma — the punctuation had fallen to the start of the line, which Japanese typography does not allow.

Rork had generated the screen from a plain request: show the quote large and centered. The code worked. The layout held. It simply was not set as Japanese text.

The iPhone 15 Pro simulator never showed it. Different width, different wrap points. I had only been lucky.

React Native's Text does not guarantee line-breaking rules

React Native's Text does not implement line breaking itself. On iOS it hands off to TextKit / CoreText. On Android it hands off to android.text.LineBreaker.

Wrap quality therefore depends on the platform and its locale resolution. That turned out to be the hard part.

EnvironmentLine-start rulesWhat I observed
iOS (default)CoreText applies some rulesCommas and periods mostly avoided. Prolonged sound mark and small kana still fall to line start
iOS with lineBreakStrategyIOS="push-out"Pushes the previous character downViolations drop sharply, but line count can grow
Android (default simple)Essentially noneCommas, periods, and closing brackets land at line start
Android with textBreakStrategy="balanced"Evens out line lengthsLooks tidier, but the rules themselves are not guaranteed

I had assumed Android would apply Japanese rules whenever the device was set to Japanese. In practice the decision comes from the locale attached to the text, not from the user's system language.

My apps ship in 16 languages. A reader looking at Japanese quotes may well have an English device. I had missed that premise entirely.

I measured the violation rate first

Fixing by feel produces the feeling of a fix and nothing else. So I took numbers.

The sample: 120 quote strings used across six wallpaper apps, including the calm and affirmation titles. Four container widths: 320, 375, 393, and 430.

onTextLayout returns each line after layout resolves. If you can read the first character of a line, you can decide the violation directly.

// KinsokuAudit.tsx — detect line-start violations at runtime
import { Text, type TextLayoutEventData, type NativeSyntheticEvent } from "react-native";
 
// Characters that must not start a line: punctuation, small kana, prolonged sound mark
const LINE_START_FORBIDDEN =
  "、。,.・:;?!ヽヾゝゞ々ー’”)〕]}〉》」』】…‥" +
  "ぁぃぅぇぉっゃゅょゎァィゥェォッャュョヮヵヶ";
 
type Violation = { lineIndex: number; head: string; lineText: string };
 
export function auditLines(
  e: NativeSyntheticEvent<TextLayoutEventData>
): Violation[] {
  const lines = e.nativeEvent.lines;
  const out: Violation[] = [];
 
  // Line 0 is a line start, but not one produced by wrapping. Skip it.
  for (let i = 1; i < lines.length; i++) {
    const text = lines[i].text ?? "";
    const head = Array.from(text)[0];
    if (!head) continue;
    if (LINE_START_FORBIDDEN.includes(head)) {
      out.push({ lineIndex: i, head, lineText: text });
    }
  }
  return out;
}
 
export function QuoteCard({ quote, onAudit }: { quote: string; onAudit?: (v: Violation[]) => void }) {
  return (
    <Text
      style={{ fontSize: 22, lineHeight: 36 }}
      onTextLayout={(e) => onAudit?.(auditLines(e))}
    >
      {quote}
    </Text>
  );
}

Skipping line 0 keeps the number meaningful: I only want line starts that wrapping created. Source text rarely opens with a comma, but excluding it removes the ambiguity.

I ran all 120 strings through four fixed-width containers. The results:

ConditionViolations / wrapped lines (iOS)Violations / wrapped lines (Android)
Plain Text (defaults)31 / 664 (4.7%)96 / 671 (14.3%)
lineBreakStrategyIOS="push-out" only9 / 682 (1.3%)96 / 671 (14.3%)
textBreakStrategy="balanced" only31 / 664 (4.7%)88 / 669 (13.2%)
Pre-binding (below)0 / 689 (0%)0 / 690 (0%)

Android's 14.3% is the number that landed. Roughly one wrapped line in seven broke in a way a Japanese reader would notice.

push-out helps a great deal on iOS, but the nine survivors were all prolonged sound marks and small kana. Leave the decision to the platform and it will not go that far for you.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Measured violation rates across 120 quotes and 4 device widths, plus the harness that produced them
A WORD JOINER pre-binding implementation that keeps copy, search, and VoiceOver intact
A CI audit that catches double-application, and where Rork Max (SwiftUI) behaves differently
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-07-10
Adding React Compiler to Expo Let Me Delete 41 Hand-Written memo Calls
I enabled React Compiler on Rork-generated React Native screens and measured the rerender counts with Profiler. Here is how I decided which memo and useCallback calls were safe to delete, how to find the components the compiler bailed out on, and how to catch regressions in CI.
Dev Tools2026-07-07
The App Icon Badge Still Says 3 — Rebuilding Expo Badge Counts Around a Single Source of Truth
Why an Expo app's icon badge drifts out of sync with real unread counts and refuses to clear — and how to rebuild it around a single source of truth, with working recompute-and-sync code and the production pitfalls that bite you.
Dev Tools2026-07-04
Should You Show a Read More Link? Let the Rendered Text Decide in Rork (Expo)
Clamping a product description to three lines and adding a Read more toggle sounds simple, until the toggle also appears under single-line text. This walks through measuring the real line count with onTextLayout so the toggle only shows when text actually overflows, covering iOS vs Android quirks, expand animation, and font scaling.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →