'Move fast & build things' with Zod, Expo & Next.js
Tags: codegen, zod, startup, trends
Published: 2022-11-13 21:59:21

Musk’s takeover of Twitter had a lot of devs looking to swoop in to build the next best thing.

While it isn’t possible to create a new Twitter app in just a few weeks ... **there ARE ways to gain enough speed to compete** with Elon's version of Twitter.

I recently delved into this during my tech talk **"Move fast & build things"**.

[![Picture of me giving my talk, title slide being shown](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3a3suihgfmgpc43s68i4.png)](https://www.linkedin.com/posts/thorr-codinsonn-dev-6951361b7_reactnative-elonmuskmemes-techtalks-activity-6996373531961782272-RRNC?utm_source=share&utm_medium=member_desktop)

I'll re-share the tips & tricks discussed during the talk here:

- ✅ How to **build for iOS, Android and the web**, from the start, in a **write-once** way. (and why you should)

- ✅ How to scale with ease, **Automate your documentation with Storybook**, and focus on building your features well and fast instead.

Why these specific topics? Well, looking at it through the same lens, let's say you have an app in mind…

## App idea 💡 New, better Twitter

So, you've chosen to compete with Elon, who is ruining the platform he bought at an alarming rate. But until it _IS_ ruined, you'll need to offer enough similar features, and Twitter has plenty.

Meaning, from the start, you already have to optimize for speed. Playing _'feature catch-up'_ requires us to literally move fast and build things.

Another thing to consider is Twitter's **reach**. It is **already available for web, iOS, and Android**. If you take yourself seriously, you'll need to launch for those platforms as well. 

Building in a **"Hybrid-first"** way becomes a must.

> Why?

Well, if you launched something users actually want to use, but it's only available on the web, you'll eventually get comments like these:

![Parody tweet by Elon Musk, taunting us and asking "Lol, mobile version when?"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jylrn117z9rcqx0nhpin.png)

While this may just be Elon taunting you as his new competitor, genuinely interested users will be asking exactly the same thing.

This should not come as a surprise, because users increasingly prefer to use the internet in Mobile apps vs Websites.

Elon has a point here. 👇

## Building for how users want to consume  

> Consider this 1-min clip from the App.js conf intro by Expo’s @ccheever

{% youtube xu9KbSOKy2M %}

### The stats don't lie:

- **More and more internet use on mobile** vs. decline on desktop
- 90% of that **mobile internet use comes from apps**
- Mobile **apps drive up to 3x more conversions** than browsers (<a href="https://branch.io/journeys/#:~:text=Your%20most%20EFFECTIVE%20conversion%20channel" target="_blank">source</a>)
- Yet we **keep** (mostly) **focusing on building websites** instead

If you REALLY want to build for how users like to consume, an app in the App + Play Store is what you should aim for.

> Consider this section from my current employer's website ([Bothrs.com](https://www.bothrs.com/service/apps))

[![Screenshot from the Bothrs website, explaining why react-native is more cost-efficient for app development](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zwmn80opteg0pjlzoi8q.png)](https://www.bothrs.com/service/apps)

Native can get you far, but since you want to move fast, building your features twice, once with Swift for iOS and once for Android, is not very efficient in terms of cost & time.

What you need if you want to build fast, is react-native with Expo.

## You can still benefit from the web

The fact that your potential users are more likely to prefer mobile, does not mean you can't still massively benefit from the web.

**Organic web search** is still a **major source of free traffic** to your application. When users are searching for answers to their problems, they will more often than not resort to Google first.

> All the more reason to show up in their search results.

With new Expo features like @evanbacon's [Expo Router](https://blog.expo.dev/announcing-the-expo-router-v1-release-candidate-c5680b88a18c), you could easily guide users landing on your site from the web, to your mobile app instead:

{% embed https://twitter.com/Baconbrix/status/1579867445611081734 %}

Ok. We're convinced we have to go cross-platform from the start. But how DO we start building for Web/ iOS/ Android with Expo?

### How to start hybrid-first? 🤔

There are a few options:

- 🤔 Web-first with e.g. `create-react-app`, add `react-native` to that setup

- 🤔 Mobile-first setup, add `react-native-web` + Next.js for SSR

- ✅ Start from a cross-platform template

The reason I don't recommend the first two options is because it **will be hard to figure out**. Trust me, I know. I've been iterating on this "server render react-native" concept since `react-native-web` and Next.js were a thing.

Let me tell you, it'll take you at least a few days, sometimes weeks before you get to the point you want your initial setup to be.

> Also, the clock is ticking, if you're going to beat Twitter, you **can't be reinventing the wheel** here.

**Choose an Expo + Next.js template instead.**

Some honorable mentions are:

- ✅ [Solito.dev](https://solito.dev/) by @nandorojo (check out his talks on Next.js & App.js conf)

- ✅ [Create-T3-Turbo](https://github.com/t3-oss/create-t3-turbo) by @t3dotgg (more opinionated, tRPC for API layer)

These solutions are pretty solid, and you _**could**_ start your 'Twitter killer' app from either of these.

> ...but...

## How to scale efficiently?

Again: **We're competing with Twitter here.** You're going to need more engineers. A bunch more.

How can you ease communication between them, and add some efficiency to a team onboarding new people?

Essentially, we want:
- 🔃 Ways to share some reusable building blocks
- 📚 Document our ways of working, preferably in 1 space
- 🤔 To not spend a lot of time writing these docs

Here too, we're trying to avoid reinventing the wheel. You don't want new developers to come in, not understand what's going on, and then decide "Meh. I could probably do this better myself."

> **No.**

If you want to go fast, never reinvent the wheel.

Storybook has a great quote for this:

![Quote: Documentation drives adoption. Storybook logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v1qgly8e4w5toqq3lbqw.png)

As good as the other templates are, this is where they tend to fall a bit short. **Docs are essential for any project you intend to ship**, even if it _were_ just a small team working on it.

Here too, there is an honorable mention: Redwood, for example, _does_ come with Storybook out of the box. Sadly, it is web-first and does not provide a way to do mobile.

If you want to work on your Twitter alternative efficiently with a lot of devs, _you're going to need another template._

## Introducing... Aetherspace

While searching for inspiration on Twitter, you stumble upon this new template to start from, made by yours truly:

{% embed https://twitter.com/codinsonn/status/1590005833970380800 %}

Curious about how this automatic generation of storybook docs works, you delve into the [quickstart](https://main--62c9a236ee16e6611d719e94.chromatic.com/?path=/story/aetherspace-quickstart--page) to find out.

## Automatic docgen with Single sources of truth

What we find is that you can kind of opt into automatic docgen by exporting a `getDocumentationProps` variable. This should contain a description of your data structure for your props and works kind of similar to how Next.js has you opt into server-side rendering with e.g. `getServerSideProps`.

> This is what the section on storybook docgen in the [Aetherspace docs](https://main--62c9a236ee16e6611d719e94.chromatic.com/?path=/story/aetherspace-quickstart--page) looks like:

![Quickstart documentation on how Aetherspace achieves automatic documentation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nz7int9yndhxbm2vuylt.png)

You think, _ok,_ so all I need to do is give the component the same name as the file, export `getDocumentationProps`, and some node script/automation will pick it up and spit out some Storybook files? That works for me! 

Some more questions do still come up though:
- What format should I export my prop structure as?
- Will I have to redefine this structure for my types?

We continue down the quickstart and find the answers to our questions:

![Quickstart documentation on how Aetherspace allows you to use actual single sources of truth](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tbszgeqyvs6vwart0c4n.png)

> This uses [Zod](https://zod.dev) for single sources of truth? Awesome, that's arguably even more type-safe than typescript itself.

You realize this is a great way to not repeat yourself when it comes to data structure and data validation. Actual "single sources of truth" you can get your type-safety (and editor hints) from, that's pretty neat 🎉 

While this makes sense to you now, you're curious what these Storybook files actually render docs-wise. So you open up Aetherspace's [example Storybook](https://main--62c9a236ee16e6611d719e94.chromatic.com/?path=/docs/features-app-core-icons) to check that out:

![Example Storybook page for a component hooking into automatic docgen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2cxcp9do6kbpode2e1j.png)

You notice that for every component opting into automatic docgen with `getDocumentationProps`, there's a markdown powered section in our Storybook with:

- ✅ **Import path** for that component + option to copy
- ✅ **Preview** of what the component looks like
- ✅ **How to use** the component in another React component
- ✅ Table of all **props, with description, defaults & controls**

You can even **edit these controls** in the table and **see the preview and code example update** live to test out what each prop ends up doing 👀

> This is a ton of value you get for describing your prop structure in a specific way.

On top of that, the template **supports adding your own Markdown** for documenting your ways of working. This should help our newly onboarded devs with knowing what's available and how to build with these blocks themselves, instead of going in blind. ✅

## Web, iOS & Android with Expo + Next.js

With docs tackled, you remember being promised an Expo + Next.js setup as well. So you check out the example page the template came with, either [online](https://aetherspace-green-stack-starter.vercel.app/) or by cloning a [demo](https://github.com/Aetherspace/green-stack-starter-demo) [repo](https://github.com/codinsonn/green-stack-demo) and running `yarn dev`:

{% embed https://aetherspace-green-stack-starter.vercel.app/ %}

![Side by side of the example Next.js and Mobile app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ec02wwy2bbfz9old8rnx.png)

> 💡 To open the iOS simulator as well, use `yarn dev:mobile` instead (with XCode installed)

You compare the web & mobile examples side by side, and notice it comes with some pretty basic template stuff:

- Image ➡️ Powered by a `src` string pointing to a local file in the public folder
- Icons for each part of the stack ➡️ Click or tap them for more info
- Which file to edit to change this example screen
- Confirmation we have some very basic APIs up and running
- Internal links to test navigation ➡️ URLs on web, stacks on mobile
- External links to e.g. the [Aetherspace docs](https://main--62c9a236ee16e6611d719e94.chromatic.com/?path=/docs/readme-md--page) ➡️ New tab or in-app browser

You decide to take the advice of being told where we can throw out all this boilerplate and start writing our own code.

## Write-once UI

It's time to write some UI for our Twitter alternative.

In the quickstart, you read up on how to do this in a write-once way:

![Aetherspace documentation on how to start writing UI in a write-once way for iOS, Android, and the web](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/316y276oqutgx2cqm51s.png)

With primitives like `Text`, `View` & `Image` you can apparently build & style your UI for all the platforms we want to target. This makes sense, you think, since all apps and components eventually are made up of them.

You do wonder why the template includes its own primitives though.

> Didn't `react-native` already have `Text`, `View` & `Image` primitives?

When you ponder a bit longer, you realize that if these primitives are optimized for each of these platforms, then anything built with those primitives should also be optimized for them as well.

To style these primitives for example, you can use tailwind, and some optimizations seem to have been made to make sure things like breakpoints and media queries work for all environments as well. (even though `react-native-web` does not support this out of the box, hey, Aetherspace has you covered)

### Truly native on mobile

Just to be sure, you have a closer look at what your react-native bundle actually looks like.

What you find is very similar to this tweet:

{% embed https://twitter.com/Baconbrix/status/1550153947524091904 %}

While the aetherspace primitives do seem to be translated by `react-native` to (e.g.) native UIKit layers under the hood, and not some sort of webview rendering a website underneath, doesn't mean they're also optimized for web however, and Elon is just waiting to call us out for it:

![Potential faked tweet by Elon, taunting us for our Google search rankings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j5anzxs416iw88aqvn6g.png)

## Semantic HTML & Web optimisations for SEO

We are competing with Twitter as a text-based platform, so ranking high in search engines will be an important factor in our plan to beat Elon at his own game.

> If you were doing e-commerce instead, this would be even more important.

Luckily, aetherspace provides you with `aetherspace/html-elements`, a small wrapper around @evanbacon's [@expo/html-elements](https://github.com/expo/expo/tree/main/packages/html-elements): 

![Aetherspace docs on opting into web optimizations like semantic HTML](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tr0hqmyxoqbw2lexidbu.png)

> Take what works and make it better

☝️ That seems to be a common theme in this aetherspace template thing. Like their `Image` primitive using `next/image` under the hood, that's bound to improve our SEO ranking:

{% embed https://twitter.com/addyosmani/status/1453059712862154755 %}

This really solidifies the reason you're choosing a template with Next.js. A close collaboration between their team and the Chrome dev team is likely to help give you a head start when it comes to search engine ranking.

Ok, but: Server rendering is nice, but what about the rest of your back-end?

## Write-once APIs

Recognizing you as competition, it doesn't take Elon long before he throws another jab your way:

![Elon asking us whether we even have an API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3qhcmliy2axxz0sbei5z.png)

Fortunately, you remember that the example screen the template came with had status badges for not just one, but TWO apis:

- ✅ REST, powered by **Next.js API routes**
- ✅ GraphQL, powered by **Apollo Server**

When you click either of these buttons, it opens their endpoints.

For the REST one, you end up at a [heath check endpoint](https://aetherspace-green-stack-starter.vercel.app/api/health) at `/api/health` that tells you the server is alive and kicking:

```json
{
  "alive": true,
  "kicking": true
}
```

When you check the [GraphQL playground](https://aetherspace-green-stack-starter.vercel.app/api/graphql) at `/api/graphql`, you see that the exact same health check is available:

![Health check query shown in the Aetherspace GraphQL Playground](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k6ycjihrrybv260qi6dp.png)

> Hmm. Are these two APIs using the same resolver underneath? Could you edit the underlying function once to (e.g.) echo back some argument and bring those API changes to both REST and GraphQL in one go...?

Once more, the quickstart provides us with the answer:



Just to recap what you just learned:

- 1. Define your data structure for input & output with [Zod](https://zod.dev)
- 2. You do this using the same `aetherSchema()` builder as for docgen
- 3. You can reuse properties from one schema in another ([and more](https://main--62c9a236ee16e6611d719e94.chromatic.com/?path=/docs/aetherspace-single-sources-of-truth--page))
- 4. Business logic is a regular promise, wrapped with `aetherResolver()`
- 5. This bundles your logic with your args & response data structure
- 6. Export a helper around that bundle to create an API route
- 7. Export a `graphQLResolver` pointing to that bundle to enable GraphQL

When you implement this echo functionality and try it out in the GraphQL playground, you notice it now also hints at the docs you defined for your arguments and response:

![New healthCheck query in apollo playground](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ll1pieh8ywjqoroanek8.jpeg)

> Another win for single sources of truth.

## More value in less time

The final part of the quickstart reflects on everything the 'Aetherspace' template helps you with:

![Aetherspace docs recapping the powerful results of this quickstart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/815h46vhl5q11spddacf.png)

> Fast forwarding our hypothetical scenario a few months: You've managed to grow your team quite a bit. Some ex-twitter engineers have even joined up. Motivated, highly efficient, and sharing knowledge through docs, your team has quickly been able to recreate a lot of core Twitter features and is ready to launch a first MVP.

## Meanwhile at Twitter HQ...

![Elon asking on twitter why it keeps crashing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evsx9nj09jqzbbxn8nyq.png)

You see, Elon made a grave mistake.

Consider this graphic of what Twitter architecture could look like after 10 years of active development:

[![Graphic of twitter architecture leaning on a tiny piece of critical infrastructure that might not have been handed over during twitter layoffs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nh3b3p9cwxbmf46n7etp.png)](https://twitter.com/cassiecodes/status/1588531575993794560)

> Thanks to xkcd and [cassiecodes](https://twitter.com/cassiecodes/status/1588531575993794560) for this graphic

Perhaps firing half your developers and watching a bunch more quit in solidarity wasn't the smartest thing to do Elon.

Eventually something important breaks, and we can all expect this to happen:

![Faked tweet of Elon Musk asking if someone wants to buy Twitter from him](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nu9ph7c4gv29ebjhc0gv.png)

When that happens, you, your team, and your alternative will be ready to swoop in and provide a fresh new experience for those fleeing Twitter.

> The end.

## Things to consider

![Disclaimer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o0xr5q9ljt9htzffw2ug.png)

> Obligatory statement to avoid getting banned from Twitter:

![Faked Elon tweet saying "This is parody"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/srdumq45u03d1dbhx2eb.png)

> Since it's just as important to know when NOT to use a tool:

🤔 If you're in need of cutting-edge native features, like building the next Spotify, this probably won't be the template for you. React-native takes a while to have all the latest native features, Expo takes some time to pick up with the latest react-native version, ... but, this _could_ be fixed with Expo's ability to add [custom native code](https://docs.expo.dev/workflow/customizing/).  

🤔 Aetherspace is a starter repo. On top of that it's currently still early days. The core features may be there and the quickstart & docs might cover a lot, but there is more testing to be done on larger-scale projects. Attempting to migrate an existing app to the template is probably a bad idea unless you’re rebranding / rebuilding it from scratch.  

🤔 If you're not a fan of Typescript, Monorepos or even React, this will also not be a good fit. ... but if you're instead familiar with another web-based front-end framework, have a look at Capacitor instead for going cross-platform from the start.  

### When to use [Aetherspace](https://github.com/Aetherspace/green-stack-starter-demo), from a business perspective:

- ✅ You want the best of both worlds. Your users may prefer mobile, but even if they don't, **you're building for both** in a very efficient way.

- ✅ You want **free organic web traffic** as a top source of lead generation. Here, Next.js = first class in SEO ranking.

- ✅ Easily guide users from web to mobile, where more conversions tend to happen.

### When to use the template, from a DEV perspective:

- ✅ You're already familiar with React, and would like to take those skills cross-platform, then this is a great way to do so.

- ✅ You want to kickstart your next Expo + Next.js project.

- ✅ You'd like to automate a large part of your documentation or explore what Storybook can do for you.

- ✅ You love single sources of truth, DRY programming and providing a ton of value in little time.

### Links & Resources

- [[Github] Aetherspace/green-stack-starter-demo](https://github.com/Aetherspace/green-stack-starter-demo)

- [[Github] codinsonn/green-stack-demo](https://github.com/codinsonn/green-stack-demo)

- [[Storybook] Template quickstart ⚡️](https://main--62c9a236ee16e6611d719e94.chromatic.com/?path=/docs/aetherspace-quickstart--page)

- [[Storybook] Aetherspace docs 📚](https://main--62c9a236ee16e6611d719e94.chromatic.com/?path=/docs/readme-md--page)

- [[Author] Social links to watch me build this in public](https://codinsonn.dev)