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".
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:
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
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 (source)
- 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)
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, you could easily guide users landing on your site from the web, to your mobile app instead:
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
, addreact-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 by @nandorojo (check out his talks on Next.js & App.js conf)
✅ 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:
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:
Curious about how this automatic generation of storybook docs works, you delve into the quickstart 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 looks like:
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:
This uses Zod 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 to check that out:
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 or by cloning a demo repo and running yarn dev
:
💡 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 ➡️ 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:
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 haveText
,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:
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:
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:
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:
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:
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 at /api/health
that tells you the server is alive and kicking:
{
"alive": true,
"kicking": true
}
When you check the GraphQL playground at /api/graphql
, you see that the exact same health check is available:
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
- 2. You do this using the same
aetherSchema()
builder as for docgen - 3. You can reuse properties from one schema in another (and more)
- 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:
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:
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...
You see, Elon made a grave mistake.
Consider this graphic of what Twitter architecture could look like after 10 years of active development:
Thanks to xkcd and cassiecodes 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:
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
Obligatory statement to avoid getting banned from Twitter:
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.
🤔 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, 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.
Top comments (0)