Vue NativeVue Native
Guide
Components
Composables
Navigation
Architecture
  • iOS
  • Android
  • macOS
GitHub
Guide
Components
Composables
Navigation
Architecture
  • iOS
  • Android
  • macOS
GitHub
  • Getting Started

    • Introduction
    • Installation
    • Your First App
    • Project Structure
  • Core Concepts

    • Components
    • Styling
    • Theming
    • TypeScript
    • Navigation
    • Navigation Components
    • Native Modules
    • Native Code Blocks
    • Hot Reload
  • UI Patterns

    • Forms and v-model
    • Shared Element Transitions
    • Teleport
    • Custom Native Components (escape hatch)
  • Quality & Debugging

    • Testing
    • Debugging
    • Error Handling
  • Platform Hardening

    • Security
    • Accessibility
    • Performance
  • Integration Guides

    • State Management
    • Deep Linking & Universal Links
    • State Persistence
    • Push Notifications
    • Error Reporting & Monitoring
  • Tooling

    • Managed Workflow
    • VS Code Extension
    • Neovim Plugin
  • Building & Releasing

    • Building Native Apps
    • Deployment & App Store Submission
  • Reference

    • Upgrade Guide
    • Migrating from React Native
    • Known Limitations & Platform Differences
    • Troubleshooting

Styling

Vue Native uses Yoga Flexbox on iOS and FlexboxLayout on Android — the same mental model as CSS Flexbox.

createStyleSheet

Use createStyleSheet to define styles as typed objects. Styles are validated and frozen for performance:

import { createStyleSheet } from '@thelacanians/vue-native-runtime'

const styles = createStyleSheet({
  container: {
    flex: 1,
    flexDirection: 'column',
    backgroundColor: '#F5F5F5',
    padding: 16,
    gap: 12,
  },
  card: {
    backgroundColor: '#FFFFFF',
    borderRadius: 12,
    padding: 16,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
  title: {
    fontSize: 20,
    fontWeight: '600',
    color: '#1A1A1A',
  },
})

Inline styles

You can also pass style objects directly:

<VView :style="{ flex: 1, padding: 16 }">
  <VText :style="{ fontSize: 16, color: '#333' }">Hello</VText>
</VView>

Units

All numeric values are in density-independent points (dp):

Platform1 dp equals
iOS @2x (iPhone SE, 8)2 physical pixels
iOS @3x (iPhone 12+)3 physical pixels
Android mdpi (160 dpi)1 physical pixel
Android xxhdpi (480 dpi)3 physical pixels

The framework automatically converts dp to pixels using the device's scale factor. You don't need to handle retina/density differences.

// 16 dp ≈ 16 CSS pixels ≈ 32-48 physical pixels depending on device
{ padding: 16, fontSize: 16, borderWidth: 1 }

Percentage values

Some layout properties accept percentage strings relative to the parent's dimension:

{
  width: '50%',       // 50% of parent's width
  height: '100%',     // 100% of parent's height
  maxWidth: '75%',    // At most 75% of parent's width
  minHeight: '25%',   // At least 25% of parent's height
}

Properties supporting percentages: width, height, minWidth, minHeight, maxWidth, maxHeight, flexBasis, top, right, bottom, left.

Note: Type definitions require casting for percentages on some properties: maxWidth: '75%' as any. This will be improved in a future release.

Breaking change in v0.8.0

padding and margin (and their padding* / margin* variants) are now numbers only — percentage strings are no longer accepted. If you previously used a percentage, compute the value from the parent dimension yourself (for example with useDimensions).

Color formats

Colors are specified as strings. Supported formats:

FormatExampleNotes
Hex (6-digit)'#FF5733'RGB
Hex (8-digit)'#FF573380'RGBA (last 2 digits = alpha)
Hex (3-digit)'#F53'Shorthand RGB
rgb()'rgb(255, 87, 51)'
rgba()'rgba(255, 87, 51, 0.5)'Alpha 0–1
Named'red', 'blue', 'transparent'CSS named colors
{
  backgroundColor: '#007AFF',
  color: 'rgba(0, 0, 0, 0.87)',
  borderColor: 'transparent',
}

Supported properties

Layout (Flexbox)

PropertyValues
flexnumber
flexDirection'row' | 'column' | 'row-reverse' | 'column-reverse'
flexWrap'wrap' | 'nowrap'
flexGrownumber
flexShrinknumber
flexBasisnumber or 'auto'
alignItems'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline'
alignSelfsame as alignItems | 'auto'
alignContent'flex-start' | 'center' | 'flex-end' | 'stretch' | 'space-between' | 'space-around'
justifyContent'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly'
width, heightnumber (dp) or '50%'
minWidth, minHeightnumber or 'auto'
maxWidth, maxHeightnumber or percentage string
aspectRationumber (e.g. 1 for square, 16/9 for widescreen)
position'relative' (default) | 'absolute'
top, right, bottom, leftnumber
padding, paddingHorizontal, paddingVertical, paddingTop, paddingBottom, paddingLeft, paddingRightnumber
paddingStart, paddingEndnumber (RTL-aware: map to left/right based on direction)
margin, marginHorizontal, marginVertical, marginTop, marginBottom, marginLeft, marginRightnumber
marginStart, marginEndnumber (RTL-aware: map to left/right based on direction)
gap, rowGap, columnGapnumber
display'flex' | 'none'
overflow'hidden' | 'visible'
direction'ltr' | 'rtl' | 'inherit'

Removed style properties (v0.8.0)

borderStyle, textDecorationStyle, and textDecorationColor were removed — they were never implemented by the native renderers. overflow: 'scroll' was also removed; overflow only accepts 'visible' or 'hidden'. For scrollable content use <VScrollView> or a list component.

Appearance

PropertyValues
backgroundColorcolor string
opacity0–1
borderRadius, borderTopLeftRadius, borderTopRightRadius, borderBottomLeftRadius, borderBottomRightRadiusnumber
borderWidth, borderTopWidth, borderRightWidth, borderBottomWidth, borderLeftWidthnumber
borderColorcolor string
overflow'hidden' | 'visible'
zIndexnumber
transformarray of transform objects

Text (on VText / VInput)

PropertyValues
fontSizenumber (dp)
fontWeight'normal' | 'bold' | '100'–'900'
fontStyle'normal' | 'italic'
colorcolor string
textAlign'left' | 'center' | 'right'
lineHeightnumber (dp)
letterSpacingnumber
textDecorationLine'underline' | 'line-through' | 'none'
textTransform'none' | 'uppercase' | 'lowercase' | 'capitalize'

Shadow (iOS)

PropertyValues
shadowColorcolor string
shadowOffset{ width: number, height: number }
shadowOpacity0–1
shadowRadiusnumber

Elevation (Android)

PropertyValues
elevationnumber (higher = more shadow)

elevation is Android-only and is a no-op on iOS/macOS. It is required for shadows to render on Android — the iOS shadow* properties have no effect there, so set elevation alongside them for cross-platform cards:

const styles = createStyleSheet({
  card: {
    backgroundColor: '#FFFFFF',
    borderRadius: 12,
    padding: 16,
    // iOS / macOS
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 8,
    // Android
    elevation: 3,
  },
})

Transforms

The transform style accepts an array of transform objects. Transforms are applied in array order and do not affect layout (the view's measured box stays the same).

{
  transform: [
    { translateX: 10 },
    { rotate: '45deg' },
    { scale: 1.2 },
  ],
}

Supported transform values

KeyTypeDescription
translateX, translateYnumberTranslation in points.
scale, scaleX, scaleYnumberScale factor (1 = unchanged).
rotate, rotateX, rotateY, rotateZstringRotation angle, e.g. '45deg' or '1.5rad'. rotateX/rotateY/rotateZ are 3D rotations around each axis.
perspectivenumberPerspective depth for 3D transforms (sets the m34 term). Larger values look flatter; smaller values exaggerate depth.
skewX, skewYstringSkew angle, e.g. '45deg'.

3D transforms

Combine perspective with rotateX / rotateY for flip and tilt effects:

const styles = createStyleSheet({
  card: {
    transform: [
      { perspective: 800 },
      { rotateY: '25deg' },
    ],
  },
})

Platform difference: skew

skewX and skewY are supported on iOS and macOS only. On Android the native View has no skew transform, so the request is logged and ignored (it will not crash). Keep skew out of cross-platform styles or guard it with selectPlatform.

Hairline borders

Use the exported hairlineWidth constant for the thinnest border the platform can render — useful for 1px-look dividers and separators:

import { createStyleSheet, hairlineWidth } from '@thelacanians/vue-native-runtime'

const styles = createStyleSheet({
  separator: {
    height: hairlineWidth,
    backgroundColor: '#C6C6C8',
  },
  bordered: {
    borderWidth: hairlineWidth,
    borderColor: '#E5E5E5',
  },
})

hairlineWidth is 0.5 and mirrors React Native's StyleSheet.hairlineWidth.

Common Layout Patterns

Center content

const styles = createStyleSheet({
  centered: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
})

Equal-width grid (2 columns)

const styles = createStyleSheet({
  grid: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    gap: 12,
  },
  gridItem: {
    width: '48%',  // Slightly less than 50% to account for gap
  },
})

Sticky header + scrollable content

const styles = createStyleSheet({
  screen: { flex: 1 },
  header: {
    padding: 16,
    backgroundColor: '#FFFFFF',
    borderBottomWidth: 1,
    borderColor: '#E5E5E5',
  },
  content: { flex: 1 },  // Applied to VScrollView
})
<VView :style="styles.screen">
  <VView :style="styles.header">
    <VText>Header</VText>
  </VView>
  <VScrollView :style="styles.content">
    <!-- Scrollable content here -->
  </VScrollView>
</VView>

Card with shadow

const styles = createStyleSheet({
  card: {
    backgroundColor: '#FFFFFF',
    borderRadius: 12,
    padding: 16,
    marginHorizontal: 16,
    marginVertical: 8,
    // iOS shadow
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 8,
    // Android shadow
    elevation: 3,
  },
})

Row with spacer (left text, right button)

const styles = createStyleSheet({
  row: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: 16,
  },
})

Theming & Dark Mode

Vue Native ships a built-in theme system: createTheme defines light/dark design tokens, <ThemeProvider> provides them via Vue's provide/inject, and createDynamicStyleSheet builds stylesheets that re-evaluate reactively when the active theme changes. Pair it with useColorScheme to follow the system dark-mode setting.

import { createTheme, createDynamicStyleSheet } from '@thelacanians/vue-native-runtime'

export const { ThemeProvider, useTheme } = createTheme({
  light: { colors: { background: '#FFFFFF', text: '#1A1A1A' }, spacing: { md: 16 } },
  dark: { colors: { background: '#000000', text: '#F5F5F5' }, spacing: { md: 16 } },
})

For the full walkthrough — token design, system sync, and persisting the user's preference — see the Theming guide.

Platform differences

PropertyiOSAndroid
shadowColor/Offset/Opacity/RadiusNative CALayer shadowNo effect (use elevation)
elevationNo effectNative View.elevation
fontWeightFull range '100'–'900'Only 'normal' and 'bold' on some devices
letterSpacingPointsTreated as em on some Android versions
Edit this page
Last Updated: 7/28/26, 4:10 PM
Contributors: github-actions[bot]
Prev
Components
Next
Theming