Universal-app-starter Alternatives
Similar projects and alternatives to universal-app-starter based on common topics and language
-
-
InfluxDB
Purpose built for real-time analytics at any scale. InfluxDB Platform is powered by columnar analytics, optimized for cost-efficient storage, and built with open data standards.
-
-
-
-
expo
An open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.
-
-
-
SaaSHub
SaaSHub - Software Alternatives and Reviews. SaaSHub helps you find the best software and product alternatives
universal-app-starter discussion
universal-app-starter reviews and mentions
-
Building a Universal React app with Expo, NextJS & Nativewind
Introduction Building universal React applications has never been easier or more efficient, thanks to Expo. Expo is a powerful toolchain that simplifies the development process, allowing developers to create high-quality, performant apps for iOS, Android, and the web with a single codebase. With this guide, we will set up a monorepo from scratch to build a Universal React app using Expo and Next.js using tools like NativeWind/Tailwind, Turborepo for building apps across both mobile and web platforms. Problem At my job, I was assigned the task of building a design system for both our mobile and web products. Given my background as a React developer, React Native was the natural choice for mobile development. The challenge was to create a shared component library with consistent styling that works seamlessly across both mobile and web applications using React and React Native. The goal was to develop a solution that supports the development of both mobile and web applications without duplicating components, rewriting business logic, or maintaining separate codebases. Before we get started, let's familiarise ourselves with some key terminologies. Universal in this case means it works on all platforms i.e Andriod, IOS, Web and others. Expo Expo is a framework that makes developing Android and iOS apps easier Next.js Next.js is a React framework for building full-stack web applications. React Native for Web Makes it possible to run React Native components and APIs on the web using React DOM. Prerequisites Node.js (>=18) Yarn (v1.22.19) Native Development Environment (Xcode, Android Studio e.t.c) Setup yarn workspaces We need to initialize our project with a package.json file yarn init Enter fullscreen mode Exit fullscreen mode Using Classic yarn as Expo documentation recommends it. We currently have first-class support for Yarn 1 (Classic) workspaces. If you want to use another tool, make sure you know how to configure it. yarn set version 1.22.19 Enter fullscreen mode Exit fullscreen mode Set private flag as true + "private": true Enter fullscreen mode Exit fullscreen mode Note that the private: true is required, Workspaces are not meant to be published. Create sub folders apps & packages "workspaces": [ "apps/*", "packages/*" ], Enter fullscreen mode Exit fullscreen mode packages/* simply means we'll reference all packages from a single directive apps contains web native packages contains ui utils e.t.c Install Turborepo turbo is built on top of Workspaces, a feature of package managers in the JavaScript ecosystem that allows you to group multiple packages in one repository. yarn add turbo --dev Enter fullscreen mode Exit fullscreen mode Add turbo.json file { "$schema": "https://turbo.build/schema.json", "tasks": { "build": { "outputs": ["dist/**", ".next/**", "!.next/cache/**"], "dependsOn": ["^build"] }, "dev": { "cache": false, "persistent": true }, "lint": {}, "clean": { "cache": false } } } Enter fullscreen mode Exit fullscreen mode Update .gitignore file + .turbo Enter fullscreen mode Exit fullscreen mode Setup Default Typescript config in the root workspace { "compilerOptions": { "strictNullChecks": true, "noUncheckedIndexedAccess": true, "baseUrl": "./packages", "paths": { "ui/*": ["./packages/ui/*"] }, "jsx": "react-jsx" }, "extends": "expo/tsconfig.base" } Enter fullscreen mode Exit fullscreen mode Setting up Packages Create new shared packages for the monorepo in packages folder ui ts-config cd into packages/ui run yarn init -y Enter fullscreen mode Exit fullscreen mode Next, create an empty index.ts in packages/ui file for now Structure of Monorepo universal-app-starter └── apps ├── native └── web └── packages ├── ui └── app Enter fullscreen mode Exit fullscreen mode Setup default apps for mobile & web with Expo & Next.js Navigate to the apps directory cd apps Enter fullscreen mode Exit fullscreen mode Setting up Nextjs app Run npx create-next-app@latest Enter fullscreen mode Exit fullscreen mode Update tsconfig.json to include "extends": "../../tsconfig.json", Enter fullscreen mode Exit fullscreen mode cd apps/web yarn run dev Enter fullscreen mode Exit fullscreen mode Using Expo npx create-expo-app@latest Enter fullscreen mode Exit fullscreen mode Next, you'll be prompted to enter your app name. Set your app name as native and run the command to reset it as fresh project yarn run reset-project Enter fullscreen mode Exit fullscreen mode Optionally, you could delete the boilerplate files & folders generated from create-expo-app /app-example. components hooks constants scripts replace tsconfig.json in the native folder { "extends": "../../tsconfig.json", "include": [ "**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts" ] } Enter fullscreen mode Exit fullscreen mode Ensure you have expo-env.d.ts file /// // NOTE: This file should not be edited and should be in your git ignore Enter fullscreen mode Exit fullscreen mode To run your project, navigate to the directory and run one of the following commands. cd native Enter fullscreen mode Exit fullscreen mode - yarn run android - yarn run ios - yarn run web Enter fullscreen mode Exit fullscreen mode Setting Up React Native Web in Next.js app In the root directory, add resolutions to package.json file "resolutions": { "react": "18.2.0", "react-native": "0.74.2", "react-native-web": "~0.19.10", "tailwindcss": "^3.4.1" } Enter fullscreen mode Exit fullscreen mode In apps/web Run yarn add react-native-web @expo/next-adapter Enter fullscreen mode Exit fullscreen mode Updating Next.js Configuration Edit next.config.js /** @type {import('next').NextConfig} */ const { withExpo } = require("@expo/next-adapter"); module.exports = withExpo({ reactStrictMode: true, transpilePackages: [ // NOTE: you need to list `react-native` because `react-native-web` is aliased to `react-native`. "react-native", "react-native-web", "ui" // Add other packages that need transpiling ], webpack: (config) => { config.resolve.alias = { ...(config.resolve.alias || {}), // Transform all direct `react-native` imports to `react-native-web` "react-native$": "react-native-web", "react-native/Libraries/Image/AssetRegistry": "react-native-web/dist/cjs/modules/AssetRegistry" // Fix for loading images in web builds with Expo-Image }; config.resolve.extensions = [ ".web.js", ".web.jsx", ".web.ts", ".web.tsx", ...config.resolve.extensions ]; return config; } }); Enter fullscreen mode Exit fullscreen mode Resetting React Native Web styles The package react-native-web builds on the assumption of reset CSS styles, here's how you reset styles in Next.js Add to globals.css html, body, #__next { width: 100%; -webkit-overflow-scrolling: touch; margin: 0px; padding: 0px; min-height: 100%; } #__next { flex-shrink: 0; flex-basis: auto; flex-direction: column; flex-grow: 1; display: flex; flex: 1; } html { -webkit-text-size-adjust: 100%; height: 100%; } body { display: flex; overflow-y: auto; overscroll-behavior-y: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -ms-overflow-style: scrollbar; } Enter fullscreen mode Exit fullscreen mode Creating first shared component Now we have RNW(React Native Web), let created our first shared component Create a file view/index.tsx and in the ui package import { View as ReactNativeView } from 'react-native' export const View = ReactNativeView; Enter fullscreen mode Exit fullscreen mode Update the packages/ui/index.ts Add export {}; Enter fullscreen mode Exit fullscreen mode Using ui package Add ui package in both native and web dependencies in package.json file .... "ui" : "*", .... Enter fullscreen mode Exit fullscreen mode Replace apps/native/index.tsx in Expo app import { Text } from "react-native"; import { View } from "ui/view"; export default function Index() { return ( Edit app/index.tsx to edit this screen.Text> View> ); } Enter fullscreen mode Exit fullscreen mode Replace apps/web/index.tsx in Next.js app "use client"; import { View } from "ui/view"; export default function Home() { return (
Get started by editing
app/page.tsxcode> p> View> ); } Enter fullscreen mode Exit fullscreen mode Configuring Metro bundler To configure a monorepo with Metro manually, there are two main changes: Wee need to make sure Metro is watching all relevant code within the monorepo, not just apps/native. cd apps/native npx expo customize metro.config.js Enter fullscreen mode Exit fullscreen mode Update metro.config.js // Learn more https://docs.expo.io/guides/customizing-metro const { getDefaultConfig } = require("expo/metro-config"); const path = require("path"); const workspaceRoot = path.resolve(__dirname, "../.."); const projectRoot = __dirname; const config = getDefaultConfig(projectRoot); config.watchFolders = [workspaceRoot]; config.resolver.nodeModulesPaths = [ path.resolve(projectRoot, "node_modules"), path.resolve(workspaceRoot, "node_modules") ]; config.resolver.disableHierarchicalLookup = true; module.exports = config; Enter fullscreen mode Exit fullscreen mode Update default entry point for Expo app Update main field in package.json in apps/native - "main": "expo-router/entry", + "main": "index.js", Enter fullscreen mode Exit fullscreen mode import { registerRootComponent } from "expo"; import { ExpoRoot } from "expo-router"; // Must be exported or Fast Refresh won't update the context export function App() { const ctx = require.context("./app"); return ; } registerRootComponent(App); Enter fullscreen mode Exit fullscreen mode Now, we have a working native & web app using shared component with RNW Results Universal Styling with NativeWind Next, we want to further and style both platform using Tailwind in Next.js and on mobile, NativeWind is the right tool to achieve. NativeWind allows you to use Tailwind CSS to style your components in React Native. Styled components can be shared between all React Native platforms. cd apps/native npx expo install nativewind@^4.0.1 react-native-reanimated tailwindcss Enter fullscreen mode Exit fullscreen mode Run pod-install to install Reanimated pod: npx pod-install Enter fullscreen mode Exit fullscreen mode Run npx tailwindcss init to create a tailwind.config.js file npx tailwindcss init Enter fullscreen mode Exit fullscreen mode Add the paths to all of your component files in your tailwind.config.js file. /** @type {import('tailwindcss').Config} */ module.exports = { content: [ + "./index.js", + "./app/**/*.{js,jsx,ts,tsx}", + "../../packages/**/*.{js,ts,jsx,tsx}" ], theme: { extend: {}, }, plugins: [], } Enter fullscreen mode Exit fullscreen mode Create a CSS file global.css and add the Tailwind directives cd apps/native touch global.css Enter fullscreen mode Exit fullscreen mode Copy & paste in global.css file @tailwind base; @tailwind components; @tailwind utilities; Enter fullscreen mode Exit fullscreen mode Add babel preset Configure babel to support NativeWind module.exports = function (api) { api.cache(true); return { presets: [ ["babel-preset-expo", { jsxImportSource: "nativewind" }], "nativewind/babel", ], }; }; Enter fullscreen mode Exit fullscreen mode Modify Metro Config +const { withNativeWind } = require('nativewind/metro'); ... -module.exports = config; +module.exports = withNativeWind(config, { input: './global.css' }) Enter fullscreen mode Exit fullscreen mode Import your CSS file In the app/layout.tsx file import "../global.css"; .... Enter fullscreen mode Exit fullscreen mode Typescript Support NativeWind extends the React Native types via declaration merging. Add triple slash directive referencing the types. Add a file app-env.d.ts in apps/native root directory. /// Enter fullscreen mode Exit fullscreen mode Next.js Support Update tailwind.config.js in apps/web import type { Config } from "tailwindcss"; const config: Config = { content: [ "./pages/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}", "./app/**/*.{js,ts,jsx,tsx,mdx}", ], + important: "html", + presets: [require('nativewind/preset')], theme: { extend: { }, }, plugins: [], }; export default config; Enter fullscreen mode Exit fullscreen mode Update tsconfig.json file { "compilerOptions": { "jsxImportSource": "nativewind" } } Enter fullscreen mode Exit fullscreen mode Using Nativewind in shared UI In packages/ui Update the view component we created earlier import { View as ReactNativeView } from 'react-native' + import { cssInterop } from 'nativewind'; + export const View = cssInterop(ReactNativeView, { + className: 'style', + }); Enter fullscreen mode Exit fullscreen mode Finally add nativewind to list of packages to transpile transpilePackages: [ ..., + "nativewind" + "react-native-css-interop" ] Enter fullscreen mode Exit fullscreen mode Update the use of the ui/view component in both web & mobile app import { Text } from "react-native"; import { View } from "ui/view"; export default function Index() { return ( + className="flex-1 justify-center items-center"' - style={{ - flex: 1, - justifyContent: "center", - alignItems: "center", - }}> ... Enter fullscreen mode Exit fullscreen mode Now, Using className works just same as with web. VSCode Intellisense Support Create a new file tailwind.config.ts in the project root directory and paste the following in the file. // Add file for tailwind intellisense. Leave this empty module.exports = {}; Enter fullscreen mode Exit fullscreen mode Troubleshooting Troubleshooting NativeWind NativeWind Next.js Common Issues if you're using typescript, confirm you have the reference to NativeWind types in app-env.d.ts /// Enter fullscreen mode Exit fullscreen mode Happy to help with any issues, be sure to leave a comment if you need help or found this useful. Links Turborepo Monorepo Guide Expo Documentation NativeWind Setup Expo Router Closing Next, you'll need to build your own or custom universal components. I'll recommend React Native Reusables to get started with most basic components. If you're interested in using an existing template, I've followed the steps from this guide to create a starter template on Github. adebayoileri / universal-app-starter Expo + Next.js (with React Native Web) template styled using TailwindCSS & NativeWind, featuring a shared component library for developing universal React applications. Universal App Starter Get Started Must have Node and Yarn(v1.22.19) installed to setup locally yarn Enter fullscreen mode Exit fullscreen mode Development yarn run dev Enter fullscreen mode Exit fullscreen mode Build yarn run build Enter fullscreen mode Exit fullscreen mode Folder Structure This monorepo consists of the two workspaces apps & packages universal-app-starter └── apps ├── native └── web └── packages ├── ui └── app Enter fullscreen mode Exit fullscreen mode Apps and Packages apps/native: a react-native app built with expo apps/web: a Next.js app built with react-native-web packages/ui: a shared package that contains shared UI components between web and native applications packages/app: a shared package that contains shared logic between web and native applications Technologies Expo for native development Next.js for web development React Native for native development React Native Web for web development NativeWind styling solution for native TypeScript for static type checking Prettier for code formatting Turborepo build system for managing monorepo Author Adebayo Ilerioluwa View on GitHub Universal App Starter
Stats
adebayoileri/universal-app-starter is an open source project licensed under MIT License which is an OSI approved license.
The primary programming language of universal-app-starter is JavaScript.