diff --git a/challenges/ecosystem/04.md b/challenges/ecosystem/04.md
index 091ff5c0..0342dd80 100644
--- a/challenges/ecosystem/04.md
+++ b/challenges/ecosystem/04.md
@@ -4,7 +4,7 @@
## 📡 What you will learn
-- Setup Storybook with React Native.
+- Setup Storybook v10 with React Native.
- Writing `.stories` to debug your application with isolated components.
## 👾 Before we start the exercise
@@ -16,7 +16,7 @@
- [ ] Setup Storybook
```console
-npx sb@latest init --type react_native
+npm create storybook@latest
```
**🔭 Hint:** Read the prompt, the setup is NOT 100% automated.
@@ -28,10 +28,19 @@ First create metro config file if you don't have it yet.
```console
npx expo customize metro.config.js
```
-Enable transformer.unstable_allowRequireContext in your metro config
+
+Then wrap your config with the `withStorybook` function and add the `enabled` option:
```js
-config.transformer.unstable_allowRequireContext = true;
+// metro.config.js
+const { getDefaultConfig } = require('expo/metro-config');
+const { withStorybook } = require('@storybook/react-native/metro/withStorybook');
+
+const config = getDefaultConfig(__dirname);
+
+module.exports = withStorybook(config, {
+ enabled: process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === 'true',
+});
```
For a more detailed guide go to:
@@ -42,29 +51,54 @@ https://github.com/storybookjs/react-native#existing-project

-- [ ] Change the entry point of your application and comment your `default export` to return Storybook's UI.
+- [ ] Change the entry point of your application to conditionally load Storybook based on the environment variable:
-```javascript
+```tsx
// App.tsx
-
-...
-
-// eslint-disable-next-line import/no-default-export
-// export default App; <------ comment this for now
-
-// return Storybook's UI
-export { default } from "./.storybook";
+import { StatusBar } from 'expo-status-bar';
+import { StyleSheet, Text, View } from 'react-native';
+
+function App() {
+ return (
+
+ Open up App.tsx to start working on your app!
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#fff',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+});
+
+let AppEntryPoint = App;
+
+if (process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === 'true') {
+ AppEntryPoint = require('./.rnstorybook').default;
+}
+
+export default AppEntryPoint;
```
-- [ ] Update your `package.json` to run storybook with `npm run storybook`:
+- [ ] Update your `package.json` to add the storybook command:
```diff
- "storybook-generate": "sb-rn-get-stories",
- "storybook-watch": "sb-rn-watcher",
-++ "storybook": "sb-rn-get-stories && expo start"
+ "scripts": {
+ "start": "expo start",
+ "android": "expo start --android",
+ "ios": "expo start --ios",
+ "web": "expo start --web",
+++ "storybook": "EXPO_PUBLIC_STORYBOOK_ENABLED=true expo start",
+ "storybook-generate": "sb-rn-get-stories"
+ },
```
-- [ ] Run your app, you should have the Storybook displayed like this:
+- [ ] Run Storybook with `npm run storybook`, you should have the Storybook displayed like this:
@@ -74,19 +108,25 @@ Right now, storybook display components created for testing purposes. We want to
> Place code as close to where it's relevant as possible
-- [ ] Delete generated `Button.stories.js` files:
+- [ ] Delete generated example stories:
```console
-rm -rf .storybook/stories/
+rm -rf .rnstorybook/stories/
```
-- [ ] Update `.storybook/main.ts` file to load stories from our components folder:
+- [ ] Update `.rnstorybook/main.ts` file to load stories from our components folder:
```diff
-module.exports = {
--- // stories: ['./stories/**/*.stories.?(ts|tsx|js|jsx)'],
-++ stories: ["../src/components/**/*.stories.?(ts|tsx|js|jsx)"],
+// .rnstorybook/main.ts
+import type { StorybookConfig } from '@storybook/react-native';
+
+const main: StorybookConfig = {
+-- stories: ['./stories/**/*.stories.?(ts|tsx|js|jsx)'],
+++ stories: ['../src/components/**/*.stories.?(ts|tsx|js|jsx)'],
+ addons: [],
};
+
+export default main;
```
- [ ] Create a new file `./src/components/Text.stories.tsx`:
@@ -155,5 +195,4 @@ Default.story = {
## 👽 Bonus
-- [ ] Update your `package.json` with `STORYBOOK_ENABLED` to [swap between React Native Storybook and your app](https://dev.to/dannyhw/how-to-swap-between-react-native-storybook-and-your-app-p3o).
- [ ] You can [watch me live coding with Dany](https://www.youtube.com/watch?v=QgYPgDxJRkU) the maintainer of Storybook React Native.
diff --git a/challenges/expo-router/01.md b/challenges/expo-router/01.md
new file mode 100644
index 00000000..235c0bbb
--- /dev/null
+++ b/challenges/expo-router/01.md
@@ -0,0 +1,168 @@
+# File-based Routing
+
+## 📡 What you will learn
+
+- Organise your routes using file-based routing.
+- How to use the `expo-router` library with the `Link` component and `useRouter` hook.
+
+## 👾 Before we start the exercise
+
+- There are others routing solutions available, keep in mind **we are using [`expo-router` library](https://docs.expo.dev/router/introduction/)**.
+- Expo Router is built on top of React Navigation, so you get the same navigation primitives with a file-based approach.
+- TypeScript is fully supported and routes are automatically typed.
+
+Here is a preview of our application user flow:
+
+
+
+## 👨🚀 Exercise 1
+
+### Installation
+
+- [ ] Read the [Getting started](https://docs.expo.dev/router/installation/) guide to:
+ 1. Install `expo-router` in your React Native project.
+ 2. Configure the entry point in `package.json` and `app.json`.
+
+**🔭 Hint:** With Expo Router, routes are automatically generated based on the file structure in the `app/` directory.
+
+### Create your first routes
+
+- [ ] Create a new `app/` directory at the root of your project.
+- [ ] Create an `app/_layout.tsx` file to define your root layout:
+
+```javascript
+// app/_layout.tsx
+import { Stack } from "expo-router";
+
+export default function RootLayout() {
+ return (
+
+
+
+
+ );
+}
+```
+
+- [ ] Create `app/index.tsx` for your `LoginScreen`.
+- [ ] Create `app/terms.tsx` for your `TermsScreen`.
+
+**🔭 Hint:** In Expo Router, `index.tsx` is the default route (like `index.html` on the web).
+
+### Navigate to another screen
+
+Do you remember the `` "by login you accept the Terms and Conditions."? We will use the `Link` component or `router.push()` to go to another screen.
+
+
+
+- [ ] Use the `Link` component from `expo-router`:
+
+```javascript
+import { Link } from "expo-router";
+
+// Option 1: Using Link component (recommended)
+
+ Terms and Conditions
+
+
+// Option 2: Using useRouter hook
+import { useRouter } from "expo-router";
+
+function LoginScreen() {
+ const router = useRouter();
+
+ function navigateToTerms() {
+ router.push("/terms");
+ }
+
+ return (
+
+ Terms and Conditions
+
+ );
+}
+```
+
+- [ ] Add a `goBack` behavior on `terms.tsx`:
+
+```javascript
+import { useRouter } from "expo-router";
+
+function TermsScreen() {
+ const router = useRouter();
+
+ return (
+