# Remix + Hygraph

## Overview

[Remix](https://remix.run/) is a modern web framework that focuses on developer experience and performance by leveraging a React-based approach to build web applications and UIs. It emphasizes server-side rendering, efficient data loading, and seamless integration with various backends and cloud environments to create fast, scalable, and interactive web experiences.

## Creating a new Remix app

To initialize a new `Remix` project using Javascript, use the JS template flag.

Open up your terminal, navigate to where you want your project, and run the following command:

```
npx create-remix@latest --template remix-run/remix/templates/remix-javascript
```
Remix app using TypeScript:
```
npx create-remix@latest
```
### Add Tailwind CSS to your Remix app

1. To add Tailwind CSS to your Remix app, run the following command:

    ```
    npx install tailwindcss postcss autoprefixer -D && npx tailwindcss init
    ```
2. Update the `tailwind.config.js` file to reflect the following:

    ```js
    /** @type {import('tailwindcss').Config} */
    export default {
      content: ["./app/**/*.{js,jsx,ts,tsx}"],
      theme: {
        extend: {}
      },
      plugins: []
    };
    ```
3. Create a `postcss.config.cjs` file in the root of your project with the following code:

    ```js
    module.exports = {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
    };
    ```
4. Update the `app/tailwind.css` file (or create it if it doesn't exist), to import Tailwind's styles:

    ```css
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    ```
5. Finally, make sure to import the `app/tailwind.css` file in your `app/root.jsx` file:
    ```js
    import "./tailwind.css";
    ```

Now, you should be able to use Tailwind CSS classes in your Remix app.

**Warning:**
We will use Remix with Tailwind CSS added in the [Getting data from Hygraph](https://hygraph.com/docs/implementations/remix/remix#getting-data-from-hygraph) section of this guide.

## Getting data from Hygraph

Now that you have created your Remix app, it's time to add some data from Hygraph. Start by cloning our demo Hygraph project:

[Click here to clone the demo project](https://app.hygraph.com/clone/751a6bdf9431476c8b82c543895e6d16?name=Implementation%20Guides)

After cloning, you will have a simple project that contains one content model called **Page** that contains fields for `Title`, `Slug`, and `Body`; and one content entry.

If you navigate to the Schema builder and click on the **Page** model, you will see this:

![Demo project - Page model](https://hygraph.com/images/docs/implementations/demo-project-page-model.png)

And if you navigate to the Content editor and view the sample entry details, you'll see this:

![Demo project - Sample content entry](https://hygraph.com/images/docs/implementations/demo-project-sample-entry.png)

## Content API

Before you can connect Remix to Hygraph, you will need to configure **Content API** access permissions for unauthenticated requests.

To do this, go **Project settings > Access > API Access > Content API** in your Hygraph project, scroll to find the **Content Permissions** box that reads **Would you like us to initialize some defaults?**, and click `Yes, initialize defaults`:

![Content API permissions](https://hygraph.com/images/docs/implementations/public-content-api-permissions.png)

Finally, copy the `High Performance Content API` endpoint from **Project settings > Access > API Access > Endpoints**. You use it to connect Remix to Hygraph later on.

## Testing a query in the API playground

Next, you should test if you can fetch the information in the sample content entry that we showed you earlier. To do this, navigate to the API Playground in your project and use the following query:

  **Query**

```graphql
query Page {
  page(where: {slug: "hygraph-works-with-any-framework"}) {
    title
    body {
      text
    }
  }
}
```

  
  **Response**

```json
{
  "data": {
    "page": {
      "title": "Hygraph works with any framework!",
      "body": {
        "text": "No matter the framework you choose, Hygraph is there for you and all your content needs. "
      }
    }
  }
}
```


If you execute the query, the response should fetch the sample query in the demo project you cloned.

## Connecting Remix to Hygraph

In `Remix`, save your `VITE_HYGRAPH_URL` in an `.env` file on the project root.

As a result, you will be able to call this key with `process.env.VITE_HYGRAPH_URL` in any of your files.

This will be where we paste [the endpoint URL we copied from Hygraph](#content-api):

```
VITE_HYGRAPH_URL=https://<HYGRAPH_CDN_LOCATION>.cdn.hygraph.com/content/<ID>/master
```
Your root directory will include the following files:
```
remix/
┣ app/
┣ node_modules/
┣ public/
┣ .env
┣ .eslintrc.cjs
┣ .gitignore
┣ README.md
┣ package-lock.json
┣ package.json
┣ postcss.config.cjs
┣ tailwind.config.js
┗ vite.config.js
```

Want to see Remix and Hygraph in action? Watch a quick video on how to connect `Remix` and `Hygraph`:

## Generating Hygraph content with Remix

[After enabling API access](#content-api), you can start using the **Pages** query with your Remix app to render content.

### Installing a GraphQL Client

GraphQL clients make communication easier by abstracting away small details and implementing additional features such as static typing of our query results.

We will use the npm package `graphql-request` because it is lightweight, has excellent TypeScript support, and supports both `Node.js` and browsers.

Use the terminal to go back to your Remix app, and type the following command:

```
npm i -D graphql-request graphql
```
This will install a GraphQL client.
### Getting a list of pages for the homepage

After installing the GraphQL client, go to the `app/routes` directory and modify or create a file called `_index.jsx` or `_index.jsx` if you are using TypeScript, and add the following code:

  **JavaScript**
```js
// File location: /app/routes/_index.jsx
// Homepage: Get a list of pages from the Hygraph
import { json } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
import { GraphQLClient, gql } from "graphql-request";

export const meta = () => {
  return [
    { title: "Hygraph Implementation Demo" },
    { name: "description", content: "Welcome to Hygraph, a headless CMS" },
  ];
};

const getPagesQuery = gql`
  {
    pages {
      slug
      title
    }
  }
`;

export let loader = async () => {
  const hygraph = new GraphQLClient(
    process.env.VITE_HYGRAPH_URL,
  );

  const { pages } = await hygraph.request(getPagesQuery);
  //console.log(pages)
  return json({ pages });
};

export default function Index() {
  let data = useLoaderData();
  return (
    <div className="m-12">
      <h1 className="mb-4 text-5xl font-bold">
        Hygraph Implementation Guides demo
      </h1>
      <p className="mb-4 text-lg">Click the links below to see other pages</p>
      <ul className="mb-8 list-disc list-inside">
      {data.pages.map(({ slug, title }) => (
        <li key={slug}>
          <Link to={`/page/${slug}`} prefetch="intent" className="underline">
            <a>{title}</a>
          </Link>
        </li>
      ))}
    </ul>
    <p>
        <button
          className="px-4 py-2 my-6 font-bold text-white bg-indigo-500 rounded hover:bg-indigo-700">
          <Link
            to ="https://hygraph.com/docs/implementations"
            target="_blank">Implementation Docs</Link>
        </button>
      </p>
    </div>
  );
}
```
  
  **TypeScript**
```ts
// File location: /app/routes/_index.tsx
// Homepage: Get a list of pages from the Hygraph
import { json } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
import { GraphQLClient, gql } from "graphql-request";

export const meta = () => {
  return [
    { title: "Hygraph Implementation Demo" },
    { name: "description", content: "Welcome to Hygraph, a headless CMS" },
  ];
};
interface Pages {
  slug: string;
  title: string;
}
const getPagesQuery = gql`
  {
    pages {
      slug
      title
    }
  }
`;
export let loader = async () => {
  const hygraph = new GraphQLClient(
    process.env.VITE_HYGRAPH_URL as string,
    {
      headers: {},
    }
  );

  const { pages }: { pages: Pages[] } = await hygraph.request(getPagesQuery);
  //console.log(pages)
  return json({ pages });
};
export default function Index() {
  let data = useLoaderData();

  return (
    <div className="m-12">
      <h1 className="mb-4 text-5xl font-bold">
        Hygraph Implementation Guides demo
      </h1>
      <p className="mb-4 text-lg">Click the links below to see other pages</p>
        <ul className="mb-8 list-disc list-inside">
        {(data as { pages: Pages[] }).pages.map(({ slug, title }) => (
          <li key={slug}>
            <Link to={`/page/${slug}`} prefetch="intent" className="underline">
              <a>{title}</a>
            </Link>
          </li>
        ))}
      </ul>
    <p>
        <button
          className="px-4 py-2 my-6 font-bold text-white bg-indigo-500 rounded hover:bg-indigo-700">
          <Link
            to ="https://hygraph.com/docs/implementations"
            target="_blank">Implementation Docs</Link>
        </button>
      </p>
    </div>
  );
}
```
  

**Code overview for the homepage**

The code you just added creates a query function that fetches a list of pages from Hygraph and renders them on the homepage as a bulleted list of links.

When you run your Remix app by using the command `npm run dev`, you should get a list of pages on the homepage.

![Homepage](https://hygraph.com/images/docs/implementations/next/next-homepage.png)

### Creating dynamic page routes with Remix

Now, that we have a list of pages in our homepage, we need to create a dynamic route for each page. To do this, create a new file in the `app/routes` directory called `page.$slug.jsx` or `page.$slug.tsx`. When finished, your source directory should resemble the following structure:

```
// Remix app directory structure
app/
┣ routes/
┃ ┣ _index.tsx
┃ ┗ page.$slug.tsx
┣ entry.client.tsx
┣ entry.server.tsx
┣ root.tsx
┗ tailwind.css
```

Add the following code to your `page.$slug.jsx` or `page.$slug.tsx` file:

  **JavaScript**
```js
// file location & name: src/routes/page/[...slug]/+page.js
// Dynamic page: Get a single page by slug from the Hygraph
import { json } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
import { GraphQLClient, gql } from "graphql-request";

export const meta = () => {
  return [
    { title: "Hygraph Implementation Demo" },
    { name: "description", content: "Welcome to Hygraph, a headless CMS" },
  ];
};

const getPageBySlug = gql`
  query Page($slug: String!) {
    page(where: { slug: $slug }) {
      title
      slug
      body {
        text
      }
    }
  }
`;
export let loader = async ({ params }) => {
  const { slug } = params;

  const hygraph = new GraphQLClient(
    process.env.VITE_HYGRAPH_URL,
  );

  const { page } = await hygraph.request(getPageBySlug, {
    slug
  });

  return json({ page });
};

export default function Page() {
  let data = useLoaderData();

  return (
    <div className="m-12">
      <h1 className="mb-4 text-5xl font-bold">{data.page.title}</h1>
      <p className="mb-8 text-lg">{data.page.body.text}</p>
      <p>
        <Link to="/" className="underline">
          Back to homepage
        </Link>
      </p>
    </div>
  );
}
```
  
  **TypeScript**
```ts
// file location & name: src/routes/page/[...slug]/+page.ts
// Dynamic page: Get a single page by slug from the Hygraph
import { json } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
import { GraphQLClient, gql } from "graphql-request";

export const meta = () => {
  return [
    { title: "Hygraph Implementation Demo" },
    { name: "description", content: "Welcome to Hygraph, a headless CMS" },
  ];
};

interface Page {
  title: string;
  slug: string;
  body: {
    text: string;
  };
}

const getPageBySlug = gql`
  query Page($slug: String!) {
    page(where: { slug: $slug }) {
      title
      slug
      body {
        text
      }
    }
  }
`;
export let loader = async ({ params }: { params: { slug: string } }) => {
  const { slug } = params;
  const hygraph = new GraphQLClient(
    process.env.VITE_HYGRAPH_URL as string,
    {
      headers: {},
    }
  );
  const { page } = await hygraph.request(getPageBySlug, {
    slug
  }) as { page: Page }; // Type assertion

  return json({ page });
};
export default function Page() {
  let data = useLoaderData() as { page: Page };
  return (
    <div className="m-12">
      <h1 className="mb-4 text-5xl font-bold">{data.page.title}</h1>
      <p className="mb-8 text-lg">{data.page.body.text}</p>
      <p>
        <Link to="/" className="underline">
          Back to homepage
        </Link>
      </p>
    </div>
  );
}
```
  

**Code overview for dynamic page route**

The code you just added will create a dynamic route for each page. It will fetch the page content from Hygraph and render it on the page.

When you run your Remix app by using the command `npm run dev`, you should be able to see the page content when you click on a page link.

![Dynamic page](https://hygraph.com/images/docs/implementations/next/next-dynamic-page.png)

Congratulations! You have a homepage and a dynamic page route to render all pages and their content.

**Pro Tip:**
This was a simple and quick example of how to fetch and render content from Hygraph using Remix.

For more complete implementation examples, [clone a starter](https://app.hygraph.com/).
