diff --git a/app.config.ts b/app.config.ts
index ce301030..1ead43fd 100644
--- a/app.config.ts
+++ b/app.config.ts
@@ -126,24 +126,30 @@ export default defineAppConfig({
         {
           title: 'Content',
           items: [
-          //   {
-          //     title: 'Learn',
-          //     url: '/learn',
-          //     rel: null,
-          //     target: null,
-          //   },
-          //   {
-          //     title: 'Build',
-          //     url: '/build',
-          //     rel: null,
-          //     target: null,
-          //   },
-          //   {
-          //     title: 'Explore',
-          //     url: '/explore',
-          //     rel: null,
-          //     target: null,
-          //   },
+            {
+              title: 'Resouces',
+              url: '/resources',
+              rel: null,
+              target: null,
+            },
+            {
+              title: 'Learn',
+              url: '/resources/learn',
+              rel: null,
+              target: null,
+            },
+            //   {
+            //     title: 'Build',
+            //     url: '/build',
+            //     rel: null,
+            //     target: null,
+            //   },
+            //   {
+            //     title: 'Explore',
+            //     url: '/explore',
+            //     rel: null,
+            //     target: null,
+            //   },
             {
               title: 'Search',
               url: '/search',
@@ -161,6 +167,12 @@ export default defineAppConfig({
               rel: 'noopener',
               target: '_blank',
             },
+            {
+              title: 'X',
+              url: 'https://x.com/unjsio',
+              rel: 'noopener',
+              target: '_blank',
+            },
             {
               title: 'GitHub',
               url: 'https://github.com/unjs',
diff --git a/content/3.resources.yml b/content/3.resources.yml
new file mode 100644
index 00000000..8ee81ad5
--- /dev/null
+++ b/content/3.resources.yml
@@ -0,0 +1,4 @@
+title: Resources
+description: Get started with UnJS, craft a project from scratch and deep dive into into packages to learn how they are built.
+navigation.icon: i-heroicons-beaker-solid
+layout: resources
diff --git a/content/3.resources/1.learn.yml b/content/3.resources/1.learn.yml
new file mode 100644
index 00000000..420b01a1
--- /dev/null
+++ b/content/3.resources/1.learn.yml
@@ -0,0 +1,5 @@
+title: Learn the basics
+description: Embark on a journey through this vast ecosystem, unraveling the mysteries and unlocking the true potential of more than twenty amazing packages.
+navigation.icon: i-heroicons-rocket-launch-solid
+navigation.description: Embark on a journey through this vast ecosystem
+layout: learn
diff --git a/content/3.resources/1.learn/1.ofetch-101-first-hand.md b/content/3.resources/1.learn/1.ofetch-101-first-hand.md
new file mode 100644
index 00000000..e840fc64
--- /dev/null
+++ b/content/3.resources/1.learn/1.ofetch-101-first-hand.md
@@ -0,0 +1,421 @@
+---
+title: ofetch 101 - first hand
+description: Discover ofetch, a better API for fetch that works on Node.js, browser, and workers.
+authors:
+  - name: Barbapapazes
+    picture: https://esteban-soubiran.site/esteban.webp
+    twitter: soubiran_
+packages:
+  - ofetch
+project: https://github.com/unjs/ofetch/tree/main/examples
+publishedAt: 2023-11-04
+modifiedAt: 2023-11-04
+layout: article
+---
+
+[ofetch](https://github.com/unjs/ofetch) is a utility package to make HTTP requests. It exists to unify the API between Node.js, browser and workers and is built on top of the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
+
+In fact, Node.js [fully supports the fetch API since version 18](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#browser_compatibility) via the [unidici project](https://github.com/nodejs/undici). If we want to support older versions, we will need to use a third-party package.
+
+At the same time, we want to be able to fetch in every environment with the same code. Code could start running in a worker and then in a browser.
+
+[ofetch](https://github.com/unjs/ofetch) has been created to simplify these tasks and provide a wrapper around the native fetch API with more options and features.
+
+## Installation
+
+First, let's create a new project:
+
+```bash
+mkdir ofetch-101
+cd ofetch-101
+npm init -y
+```
+
+Then, install the package:
+
+```bash
+npm install ofetch
+```
+
+::alert{type="info"}
+We can use the package manager of our choice like `npm`, `yarn`, `pnpm` or `bun`.
+::
+
+## First request
+
+In order to make our first request, let's create a file named `first-request.mjs` with the following content:
+
+```js [first-request.mjs]
+import { ofetch } from 'ofetch'
+
+const data = await ofetch('https://ungh.cc/repos/unjs/ofetch')
+```
+
+Then, we can run the script with:
+
+```bash
+node first-request.mjs
+```
+
+And _voilà_, we should see something like this:
+
+```sh
+{
+  repo: {
+    id: 319960543,
+    name: 'ofetch',
+    repo: 'unjs/ofetch',
+    description: '😱 A better fetch API. Works on node, browser and workers.',
+    createdAt: '2020-12-09T13:11:38Z',
+    updatedAt: '2023-09-04T17:23:36Z',
+    pushedAt: '2023-09-04T21:05:19Z',
+    stars: 2474,
+    watchers: 14,
+    forks: 80,
+    defaultBranch: 'main'
+  }
+}
+```
+
+We have made our first request with [ofetch](https://github.com/unjs/ofetch)! :tada: Easy, right?
+
+### Manual parsing
+
+In our first example, the returned result is automatically parsed to JSON thanks to [unjs/destr](https://github.com/unjs/destr).
+
+<!-- TODO: add related article to 101 destr -->
+
+This behavior is very useful but sometimes, we want to manually parse the result.
+
+The first way to change how received data is parsed is using the `responseType` option. [ofetch](https://github.com/unjs/ofetch) provides several options:
+
+- `text` to have the raw text
+- `json` to have a JSON object
+- `blob` to have a `Blob` object
+- `arrayBuffer` to have an `ArrayBuffer` object
+- `stream` to have a `ReadableStream` object
+
+To try it, let's create a new file named `manual-parsing.mjs`.
+
+```js [manual-parsing.mjs]
+import { ofetch } from 'ofetch'
+
+const data = await ofetch('https://ungh.cc/repos/unjs/ofetch', {
+  responseType: 'blob',
+})
+
+console.log(data) // A Blob object is returned.
+```
+
+We can also provide a custom function to parse the result using the `parseFunction` option. By default, [ofetch](https://github.com/unjs/ofetch) uses [unjs/destr](https://github.com/unjs/destr). Let's make our custom parser by creating a new file named `custom-parsing.mjs`.
+
+```js [custom-parsing.mjs]
+import { ofetch } from 'ofetch'
+
+const data = await ofetch('https://ungh.cc/repos/unjs/ofetch', {
+  parseFunction: (data) => {
+    return data // We return the raw data but we could use JSON.parse to return a JSON object.
+  },
+})
+
+console.log(data) // The data is not parsed.
+```
+
+The function takes the raw data as an argument and must return something, ideally, data parsed.
+
+## Type safety
+
+::alert{type="info"}
+Usage of TypeScript is not mandatory to use [ofetch](https://github.com/unjs/ofetch).
+::
+
+Thanks to TypeScript and generics, [ofetch](https://github.com/unjs/ofetch) can be type safe. In order to easily use TypeScript to run our scripts, we will use [unjs/jiti](https://github.com/unjs/jiti).
+
+<!-- TODO: add related article to 101 jiti -->
+
+We can use it using `npx`:
+
+```bash
+npx jiti <filename>.ts
+```
+
+Now that we are ready, let's create another file called `type-safety.ts` to type safe our [ofetch](https://github.com/unjs/ofetch) request:
+
+```bash
+touch type-safety.ts
+```
+
+Then, let's create an interface for the response. We will use the same request as in our first request.
+
+```ts [type-safety.ts]
+interface Repo {
+  id: number
+  name: string
+  repo: string
+  description: string
+  stars: number
+}
+```
+
+Now, we can use the [ofetch](https://github.com/unjs/ofetch) function, like in our `first-request.ts` file, but this time, we will add a generic type to it.
+
+```ts [type-safety.ts]
+import { ofetch } from 'ofetch'
+
+interface Repo {
+  id: number
+  name: string
+  repo: string
+  description: string
+  stars: number
+}
+
+async function main() {
+  const { repo } = await ofetch<{ repo: Repo }>('https://ungh.cc/api/github/unjs/ofetch')
+
+  console.log(`The repo ${repo.name} has ${repo.stars} stars.`) // The repo object is now strongly typed.
+}
+
+main().catch(console.error)
+```
+
+Nothing more to have a fully typed repo object! :ok_hand:
+
+::alert{type="warning"}
+This is **not a validation**. It's just a way to tell TypeScript what the response should look like. If we want full validation, we can use a tool like [zod](https://zod.dev) or [valibot](https://valibot.dev).
+::
+
+## Options
+
+With [ofetch](https://github.com/unjs/ofetch), we can do much more than simple `GET` requests. Let's see how to use and combine options to make more complex requests.
+
+### Methods
+
+[ofetch](https://github.com/unjs/ofetch) supports all the HTTP methods. Let's see how to use one of them. To test a `POST` request, we will use the GitHub API. Let's create a new file named `methods.mjs` with the following content:
+
+```js [methods.mjs]
+import { ofetch } from 'ofetch'
+
+const response = await ofetch('https://api.github.com/gists', {
+  method: 'POST',
+}) // Be careful, we use the GitHub API.
+
+console.log(response)
+```
+
+Of course, we receive an `401 Unauthorized` error because we didn't provide any authentication. But this error means we have successfully made a `POST` request.
+
+Supported methods are:
+
+- `GET`
+- `POST`
+- `PUT`
+- `PATCH`
+- `DELETE`
+- `HEAD`
+- `OPTIONS`
+
+### Body
+
+Of course, when the method is not `GET` or `HEAD`, we can provide a body to the request. Let's see how to do it by creating a new file named `body.mjs` with the following content:
+
+```js [body.mjs]
+import { ofetch } from 'ofetch'
+
+const response = await ofetch('https://api.github.com/markdown', {
+  method: 'POST',
+  // To provide a body, we need to use the `body` option and just use an object.
+  body: {
+    text: 'UnJS is **awesome**!\n\nCheck out their [website](https://unjs.io).',
+  },
+}) // Be careful, we use the GitHub API.
+
+console.log(response)
+```
+
+And we receive:
+
+```html
+<p>UnJS is <strong>awesome</strong>!</p>
+<p>Check out their <a href="https://unjs.io" rel="nofollow">website</a>.</p>
+```
+
+Perfect and easy!
+
+The body can take several formats:
+
+- `string`
+- `FormData`
+- `URLSearchParams`
+- `Blob`
+- `BufferSource`
+- `ReadableStream`
+- `Record<string, any>`
+
+### Query string
+
+We can also provide some query string parameters to the request. They can be useful to add filters or to paginate the results.
+
+Imagine we want to get the last 2 tags of the [`unjs/ofetch`](https://github.com/unjs/ofetch) repository. We can do it by creating a new file named `query-string.mjs` with the following content:
+
+```js [query-string.mjs]
+import { ofetch } from 'ofetch'
+
+const response = await ofetch('https://api.github.com/repos/unjs/ofetch/tags', {
+  query: {
+    per_page: 2,
+  },
+}) // Be careful, we use the GitHub API directly.
+
+console.log(response)
+```
+
+We receive this response (results could be different, because the tags are updated):
+
+```js
+[
+  {
+    name: 'v1.3.3',
+    zipball_url: 'https://api.github.com/repos/unjs/ofetch/zipball/refs/tags/v1.3.3',
+    tarball_url: 'https://api.github.com/repos/unjs/ofetch/tarball/refs/tags/v1.3.3',
+    commit: {
+      sha: '051ef83ceba6cc496ac487969c9f6611ef6bd10d',
+      url: 'https://api.github.com/repos/unjs/ofetch/commits/051ef83ceba6cc496ac487969c9f6611ef6bd10d'
+    },
+    node_id: 'MDM6UmVmMzE5OTYwNTQzOnJlZnMvdGFncy92MS4zLjM='
+  },
+  {
+    name: 'v1.3.2',
+    zipball_url: 'https://api.github.com/repos/unjs/ofetch/zipball/refs/tags/v1.3.2',
+    tarball_url: 'https://api.github.com/repos/unjs/ofetch/tarball/refs/tags/v1.3.2',
+    commit: {
+      sha: '1258d51494656c9507a8bb82646c644c64f23e69',
+      url: 'https://api.github.com/repos/unjs/ofetch/commits/1258d51494656c9507a8bb82646c644c64f23e69'
+    },
+    node_id: 'MDM6UmVmMzE5OTYwNTQzOnJlZnMvdGFncy92MS4zLjI='
+  }
+]
+```
+
+A query string is an object of type `{ [key: string]: any }` so it's easy to use.
+
+::alert{type="info"}
+We can find a key `params` in the options. It's an alias for `query`.
+::
+
+### Headers
+
+In the [methods section](#methods), we encountered an error because we didn't provide any authentication. With headers, we can solve our problem. In fact, we need to provide to GitHub an authentication token in a header named `Authorization` with a value of `token <token>`. Let's see how to do it by creating a new file named `headers.mjs` with the following content:
+
+```js [headers.mjs]
+import { ofetch } from 'ofetch'
+
+const response = await ofetch('https://api.github.com/gists', {
+  method: 'POST',
+  headers: {
+    Authorization: `token ${process.env.GH_TOKEN}`,
+  }
+}) // Be careful, we use the GitHub API directly.
+
+console.log(response)
+```
+
+::alert{type="info"}
+We can generate a new GitHub Token with the [GitHub CLI](https://cli.github.com): `gh auth token`.
+::
+
+We can run the script with:
+
+```bash
+GH_TOKEN=<token> node headers.mjs
+```
+
+And we get the following error:
+
+```bash
+FetchError: [POST] "https://api.github.com/gists": 422 Unprocessable Entity
+```
+
+And that's a good thing! It means we have successfully authenticated to GitHub. We just need to provide a valid body to the request but now, we know how to do it! We have to add the `body` option to the request with correct data:
+
+```js [headers.mjs]
+import { ofetch } from 'ofetch'
+
+const response = await ofetch('https://api.github.com/gists', {
+  method: 'POST',
+  headers: {
+    Authorization: `token ${process.env.GH_TOKEN}`,
+  },
+  body: {
+    description: 'This is a gist created by ofetch.',
+    public: true,
+    files: {
+      'unjs.txt': {
+        content: 'UnJS is awesome!',
+      }
+    }
+  }
+}) // Be careful, we use the GitHub API directly.
+
+console.log(response.url)
+```
+
+Run the script again:
+
+```bash
+GH_TOKEN=<token> node headers.mjs
+```
+
+And as a result, we get the URL of the created gist! :raised_hands:
+
+::alert{type="danger"}
+Never put credential token directly in code. Use an environment variable instead.
+::
+
+### Error handling
+
+This part is straightforward with [ofetch](https://github.com/unjs/ofetch). Surround the HTTP call with a `try/catch` block and we're done. Let's see how to do it by creating a new file named `error-handling.ts` with the following content:
+
+```js [error-handling.mjs]
+import { ofetch } from 'ofetch'
+
+try {
+  await ofetch('https://api.github.com', {
+    method: 'POST',
+  })
+}
+catch (error) {
+  // Error will be pretty printed
+  console.error(error)
+}
+```
+
+With that, we will only see a `FetchError` in the console. We can do better. In fact, we can retrieve the returned error body using `error.data`. Let's update our script a little:
+
+```js [error-handling.mjs]
+// ...
+
+try {
+  // ...
+}
+catch (error) {
+  console.log(error.data) // This allows us to get the error body.
+}
+```
+
+Then, run the script and we get the following error that we can easily use to display a message to the user:
+
+```sh
+{
+  message: 'Not Found',
+  documentation_url: 'https://docs.github.com/rest/reference/repos#create-a-repository-for-the-authenticated-user'
+}
+```
+
+We can also completely disable error response using the option `ignoreResponseError`.
+
+## Conclusion
+
+We have seen how to use [ofetch](https://github.com/unjs/ofetch) to easily make HTTP requests in any environment. We have seen how to use the different options to make more complex requests and how to gracefully handle errors.
+
+With this 101, we've just scratched the surface of [ofetch](https://github.com/unjs/ofetch). In the next articles, we will use some advanced options like `retry` and `timeout` to make our requests more robust, interceptors to make optimistic updates, and how to create a fetch with some default options!
diff --git a/content/4.packages/.template.yml b/content/4.packages/.template.yml
index 29e292cb..0fc62c87 100644
--- a/content/4.packages/.template.yml
+++ b/content/4.packages/.template.yml
@@ -7,4 +7,5 @@ npm:
   name: npm_name
 playgrounds:
 documentation: docs_link
+examples:
 layout: package
diff --git a/content/4.packages/ofetch.yml b/content/4.packages/ofetch.yml
index 37d0e866..370d5da8 100644
--- a/content/4.packages/ofetch.yml
+++ b/content/4.packages/ofetch.yml
@@ -6,4 +6,5 @@ github:
 npm:
   name: ofetch
 documentation: https://github.com/unjs/ofetch#ofetch
+examples: https://github.com/unjs/ofetch/tree/main/examples
 layout: package
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 2527f420..6a955398 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -20,7 +20,7 @@ export default <Partial<Config>> {
         },
       },
       fontFamily: {
-        sans: ['Nunito', 'Nunito fallback' , ...fontFamily.sans],
+        sans: ['Nunito', 'Nunito fallback', ...fontFamily.sans],
       },
       typography: theme => ({
         DEFAULT: {