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

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

    • Components
    • Styling
    • Navigation
    • Native Modules
    • Native Code Blocks
    • Hot Reload
  • Advanced

    • Error Handling
    • Accessibility
    • TypeScript
    • Performance
    • Shared Element Transitions
    • Testing
    • Security
    • Debugging
    • Teleport
    • Forms and v-model
  • 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 for Release
    • Deployment & App Store Submission
  • Reference

    • Migration & Upgrade Guide
    • 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, margin*, padding*.

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

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
margin, marginHorizontal, marginVertical, marginTop, marginBottom, marginLeft, marginRightnumber
gap, rowGap, columnGapnumber
display'flex' | 'none'
overflow'hidden' | 'visible' | 'scroll'
direction'ltr' | 'rtl' | 'inherit'

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)

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 provides a built-in theme system via createTheme:

// theme.ts
import { createTheme } from '@thelacanians/vue-native-runtime'

export const { ThemeProvider, useTheme } = createTheme({
  light: {
    colors: {
      background: '#FFFFFF',
      surface: '#F5F5F5',
      text: '#1A1A1A',
      textSecondary: '#8E8E93',
      primary: '#007AFF',
      error: '#FF3B30',
    },
    spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
    borderRadius: { sm: 4, md: 8, lg: 12, xl: 16 },
  },
  dark: {
    colors: {
      background: '#000000',
      surface: '#1C1C1E',
      text: '#F5F5F5',
      textSecondary: '#8E8E93',
      primary: '#0A84FF',
      error: '#FF453A',
    },
    spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
    borderRadius: { sm: 4, md: 8, lg: 12, xl: 16 },
  },
})

Wrap your app root:

<!-- App.vue -->
<template>
  <ThemeProvider>
    <RouterView />
  </ThemeProvider>
</template>

Use createDynamicStyleSheet for theme-aware styles that update reactively:

<script setup>
import { useTheme } from '../theme'
import { createDynamicStyleSheet } from '@thelacanians/vue-native-runtime'

const { theme, colorScheme, toggleColorScheme } = useTheme()

const styles = createDynamicStyleSheet(theme, (t) => ({
  container: {
    flex: 1,
    backgroundColor: t.colors.background,
    padding: t.spacing.md,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: t.colors.text,
  },
  card: {
    backgroundColor: t.colors.surface,
    borderRadius: t.borderRadius.lg,
    padding: t.spacing.md,
  },
}))
</script>

<template>
  <VView :style="styles.container">
    <VText :style="styles.title">{{ colorScheme }} mode</VText>
    <VButton :onPress="toggleColorScheme">
      <VText>Toggle Theme</VText>
    </VButton>
  </VView>
</template>

Syncing with system dark mode

Use useColorScheme to detect the system setting and sync with the theme:

<script setup>
import { watch } from '@thelacanians/vue-native-runtime'
import { useColorScheme } from '@thelacanians/vue-native-runtime'
import { useTheme } from '../theme'

const { colorScheme: systemScheme } = useColorScheme()
const { setColorScheme } = useTheme()

// Sync theme with system setting
watch(() => systemScheme.value, (scheme) => {
  if (scheme) setColorScheme(scheme)
}, { immediate: true })
</script>

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: 2/28/26, 11:24 PM
Contributors: Abdul Hamid, Claude Opus 4.6
Prev
Components
Next
Navigation