Simple, light-weight, and well tested, universal semantic HTML elements as React components for iOS, Android, web, and desktop apps!
We at Expo recommend using platform agnostic primitives like `View`, `Image`, and `Text` whenever possible but sometimes that's not easy. Some primitives like Tables, and Footers are native to web only and currently have no way of easily accessing. This package aims to solve that while still being an optimal UI package for iOS, and Android.
### What you get
- Using `@expo/html-elements` will optimize your website for SEO and accessibility. Meaning your websites are indexed more accurately and your native apps better accommodate physically impaired users.
- This package takes full advantage of [`react-native-web` a11y rules](https://github.com/necolas/react-native-web/blob/master/packages/docs/src/guides/accessibility.stories.mdx) whenever possible.
- For example, the `H1` component will render an `` on web, a `UILabel` on iOS, and a `TextView` on Android.
- Every component can accept styles from the `StyleSheet` API.
- TypeScript works for iOS, Android, and web, no more having to create monkey patches to use `href` on a `Text` element.
- Every component is tested render **tested universally** for iOS, Android, and Web using the package [`jest-expo-enzyme`](https://www.npmjs.com/package/jest-expo-enzyme). Each element is also **E2E tested** on iOS with Detox, and web with [`jest-expo-puppeteer`](https://www.npmjs.com/package/jest-expo-puppeteer).
- This package is completely side-effect free!
## Setup
Install:
```sh
yarn add @expo/html-elements
```
Import and use the package:
```tsx
import { H1 } from '@expo/html-elements';
```
# Components
Here is a list of all the currently supported elements and the web feature they map to. Not all HTML elements are supported. There are some HTML elements that mostly overlap with some universal modules, you should always try to use the universal modules whenever possible. All supported components are a capitalized variation of the semantic HTML they implement/emulate.
| HTML | `@expo/html-elements` |
| ----------------------------------- | :-----------------------------: |
| [``][html-a] | [``](#a) |
| [``][html-article] | [``](#article) |
| [``][html-aside] | [``](#aside) |
| [``][html-b] | [``](#b) |
| [``][html-blockquote] | [``](#blockquote) |
| [` `][html-br] | [` `](#br) |
| [`
Example
```
| Platform | Output |
| -------- | ------------------------------------------------------ |
| Web | `` |
| Native | `` |
## Link
### ``
You can use the anchor element with href prop to open links. On native this will attempt to use the `Linking` API to open the `href`.
- The CSS style is fully normalized to match ``
- For pseudo-class effects like hover and focus states check out the package [`react-native-web-hooks`](https://www.npmjs.com/package/react-native-web-hooks) | [tutorial](https://blog.expo.dev/css-pseudo-class-effects-in-expo-for-web-56649f88eb6b)
```tsx
import { A } from '@expo/html-elements';
export default () => ;
}
```
| Platform | Output |
| -------- | ------------------------------------------------------------------------------- |
| Web | `` |
| Native | `` |
## Layout
You can use layout elements like Header, Main, Footer, Section, Nav, etc. as a drop-in replacement for `View`s in your existing app.
#### Default Layout style
All layout HTML elements inherit the shared style of `` to accommodate the [Yoga layout engine][yoga] which we use on native for iOS, and Android.
- `display` is always `flex`. This is because [Yoga][yoga] only implements `display: flex`.
- `flex-direction` is always `column` instead of `row`.
#### Why use Layout elements
Consider the following: in your app you have a basic element at the top which wraps the buttons and title. A screen reader doesn't understand that this is a header, and mostly neither does a web crawler. But if you replace the encasing view with a `` the following happens:
- **iOS**: `UIView` uses [`UIAccessibilityTraitHeader`](https://developer.apple.com/documentation/uikit/uiaccessibilitytraitheader?language=objc).
- **Android**: `View` will use the proper [`AccessibilityNodeInfoCompat.CollectionItemInfoCompat`](https://github.com/facebook/react-native/blob/7428271995adf21b2b31b188ed83b785ce1e9189/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java#L370-L372) | [docs](https://developer.android.com/reference/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.CollectionItemInfoCompat).
- **web**: render an HTML 5 [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header) with the ARIA `role` set to [`"banner"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Banner_role).
Some elements like `Footer` and `Main` have no iOS, or Android enhancements, but they'll still improve web. Using the proper HTML 5 elements will make your layout compliant with the [HTML5 outline algorithm](https://html.spec.whatwg.org/multipage/sections.html#outlines).
### ``
```tsx
import { Nav } from '@expo/html-elements';
export default () => ;
```
| Platform | Output |
| -------- | ------------------------------ |
| Web | `` |
| Native | `` |
### ``
Renders a `` on web with ARIA set to [`banner`][aria-banner] and a `View` with ARIA set to `header` on mobile.
```tsx
import { Header } from '@expo/html-elements';
export default () => ;
```
| Platform | Output |
| -------- | ------------------------------------------------------------------------------------------------------------------- |
| Web | [``][html-header] |
| Native | `` |
| iOS | `UIView` uses [`UIAccessibilityTraitHeader`][uiatheader]. |
| Android | `View` will use the proper [`AccessibilityNodeInfoCompat.CollectionItemInfoCompat`][anicompat] [docs][anicompatdoc] |
[uiatheader]: https://developer.apple.com/documentation/uikit/uiaccessibilitytraitheader?language=objc
[anicompat]: https://github.com/facebook/react-native/blob/7428271995adf21b2b31b188ed83b785ce1e9189/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java#L370-L372
[anicompatdoc]: https://developer.android.com/reference/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.CollectionItemInfoCompat
### ``
Renders a `` on web with ARIA `role` set to `main` and a `View` with no ARIA set on mobile.
```tsx
import { Main } from '@expo/html-elements';
export default () => (
Some content in the main element
);
)
```
| Platform | Output |
| -------- | ------------------------------------------- |
| Web | `` |
| Native | `` |
### ``
Renders a `` on web with ARIA set to `region` and a `View` with ARIA set to `summary` on mobile.
```tsx
import { Section } from '@expo/html-elements';
export default () => ;
```
| Platform | Output |
| -------- | ------------------------------------------------ |
| Web | `` |
| Native | `` |
### ``
Renders an `` on web and a `View` everywhere else.
```tsx
import { Article } from '@expo/html-elements';
export default () => ;
```
| Platform | Output |
| -------- | ---------------------------- |
| Web | `` |
| Native | `` |
### ``
```tsx
import { Aside } from '@expo/html-elements';
export default () => ;
```
| Platform | Output |
| -------- | -------------------------------- |
| Web | `` |
| Native | `` |
### ``
Renders an `` on web and a `View` everywhere else.
```tsx
import { Footer } from '@expo/html-elements';
export default () => ;
```
| Platform | Output |
| -------- | ------------------------------- |
| Web | `` |
| Native | `` |
## Text
Text elements currently use `Text` universally rendering either a `div` or `span` to emulate Yoga style properly.
- Style is modified to match web.
- All font styles are reset (minus `Code`, and `Pre`).
- All elements accept styles from `StyleSheet` API.
```tsx
import { P, B, S, I, BR, Code } from '@expo/html-elements';
export default () => (
<>
```
| Platform | Output |
| --------- | ----------------------------------------- |
| Universal | `` \| `` |
### ``
Highlight text.
| Platform | Output |
| --------- | ---------------------------------------------------------------- |
| Universal | `` |
### ``
Quoted text.
| Platform | Output |
| --------- | -------------------------------------------------- |
| Universal | `"{props.children}"` |
### ``
| Platform | Output |
| --------- | --------------------------- |
| Universal | `` |
### ``
- `dateTime` prop is supported on web and stripped on native.
| Platform | Output |
| --------- | --------------------------- |
| Universal | `` |
## Lists
Lists can be used to create basic bulleted or numbered lists. You should try and use universal `FlatList` or `SectionList` components for long scrolling lists instead of these.
### `
`
Create an unordered (bulleted) list `
` on web, and emulates the style with a `` on native.
- [x] Resets font styles everywhere.
- [ ] Supports i18n by reversing format on iOS and Android
- [ ] Supports custom bullets
```tsx
import { UL, LI } from '@expo/html-elements';
export default () => (
` |
| Native | `` |
### ``
Create a standard list item `` on web and a native view on mobile which can render text or views inside it.
| Platform | Output |
| -------- | ---------------------------------------------------------- |
| Web | `` |
| Native | `` \| `` |
## Rules
### ``
Renders a `` everywhere. Style is modified to match web.
```tsx
import { HR } from '@expo/html-elements';
export default () => ;
```
| Platform | Output |
| -------- | --------------------------- |
| Web | `` |
| Native | `` |
### ` `
Create a line break.
| Platform | Output |
| -------- | ---------------------------------------- |
| Web | ` ` |
| Native | `` |
## Tables
Create tables universally.
- Each element renders to the expected type on web.
- `padding` is removed from all table elements.
- Text **can only** be rendered in `TH` and `TD` on mobile.
- `colSpan` and `rowSpan` are currently web-only (PRs welcome).
```tsx
import { Table, THead, TH, TBody, TFoot, TR, TD, Caption } from '@expo/html-elements';
import { Text } from 'react-native';
export default () => (
Caption
The table header
The table body
with two columns
This is the table footer
);
```
#### Table example output web
```html
Caption
The table header
The table body
with two columns
The table body
```
### `
`
Base element for creating a Table.
| Platform | Output |
| -------- | --------------------------- |
| Web | `
` |
| Native | `` |
### ``
Header element in a Table.
| Platform | Output |
| -------- | --------------------------- |
| Web | `` |
| Native | `` |
### ``
Body element in a Table.
| Platform | Output |
| -------- | --------------------------- |
| Web | `` |
| Native | `` |
### ``
Footer element in a Table.
| Platform | Output |
| -------- | --------------------------- |
| Web | `` |
| Native | `` |
### `
`
Used to display text in the Header.
- `colSpan` and `rowSpan` are currently web-only.
| Platform | Output |
| -------- | --------------------------- |
| Web | `
` |
| Native | `` |
### `
`
Used to create a Row in a Table.
| Platform | Output |
| -------- | --------------------------- |
| Web | `
` |
| Native | `` |
### `
`
Create a cell in a Table.
- `colSpan` and `rowSpan` are currently web-only.
| Platform | Output |
| -------- | --------------------------- |
| Web | `
` |
| Native | `` |
### `
`
Used to caption your table. Excepts text as a child.
| Platform | Output |
| -------- | --------------------------- |
| Web | `