import nextCallout from "/src/fragments/lib/ssr/js/next-js-callout.mdx";
To work well with server-rendered pages, Amplify JS requires slight modifications from how you would use it in a client-only environment.
## Amplify
### Enabling SSR
When using the Amplify CLI, the __aws-exports.js__ file gets created and updated automatically for you based upon the resources you have added and configured.
For client-only apps, `Amplify.configure(awsExports)` is all you need.
To enable SSR support, also provide `ssr: true`:
```js
import { Amplify } from "aws-amplify";
import awsExports from "../src/aws-exports";
Amplify.configure({ ...awsExports, ssr: true });
```
By providing `ssr: true`, Amplify persists credentials on the client in cookies so that subsequent requests to the server have access to them.
> **Note**: Once [vercel/next.js#16977](https://github.com/vercel/next.js/issues/16977) is resolved, you can hoist `Amplify.configure` into **pages/_app.js**. Until then, be sure that all **pages/*** run `Amplify.configure({ ...awsExports, ssr: true })`.
### withSSRContext
Once an application has been configured with `ssr: true`, client-side credentials are passed to the server via cookies.
The `withSSRContext` utility creates an instance of `Amplify` scoped to a single request (`req`) using those cookie credentials.
For client-side code rendered in the browser, your page should continue using top-level imports as usual:
```js
import { Amplify, API } from "aws-amplify";
import awsExports from "../src/aws-exports";
Amplify.configure({ ...awsExports, ssr: true });
export default function HomePage({ posts = [] }) {
const [posts, setPosts] = useState(posts);
useEffect(() => {
// Notice how the client correctly uses the top-level `API` import
API.graphql({ query: listPosts }).then(({ data }) => setPosts(data.listPosts.items));
}, [])
return ( ... );
}
```
When on the server, use `withSSRContext({ req?: ServerRequest })`:
```js
import { Amplify, API, withSSRContext } from "aws-amplify";
export async function getServerSideProps({ req }) {
// Notice how the server uses `API` from `withSSRContext`, instead of the top-level `API`.
const SSR = withSSRContext({ req })
const { data } = await SSR.API.graphql({ query: listPosts });
return {
props: {
posts: data.listPosts.items
}
}
}
```
Server-side functions that don't have a req object (e.g. Next.js' getStaticProps & getStaticPaths) should still use withSSRContext(). Please note that it's not possible to perform authenticated requests with Amplify when using these functions.
## Use Amplify with Next.js App Router ("app/" directory)
Amplify JavaScript can be used with the Next.js App Router (Next.js v13.4+) by applying the following changes:
### 1. Run `Amplify.configure({ ...awsExports, ssr: true })` in **both** the client-side and server-side code
To use Amplify with Next.js App Router, you **must** run `Amplify.configure()` in both Client and Server Components. The `ssr` option **must** be enabled.
Example Next.js App Router Client Component containing your login flow (e.g. `app/page.tsx`):
```js
'use client';
import { Amplify } from 'aws-amplify';
import awsExports from '@/aws-exports';
Amplify.configure({ ...awsExports, ssr: true });
export default function Home() {
return (
{/* Render a login form for your users */}
)
}
```
Example Next.js App Router Server Component (e.g. `app/posts/page.tsx`):
```js
import { Amplify } from 'aws-amplify';
import awsExports from '@/aws-exports';
Amplify.configure({ ...awsExports, ssr: true });
export default async function Page() {
// ...
}
```
### 2. Prepare a request object for `withSSRContext` to perform server-side operations that require authentication
Example Next.js App Router Server Component that queries data from GraphQL (e.g. `app/posts/page.tsx`):
```js
import { Amplify, withSSRContext } from 'aws-amplify';
import { headers } from 'next/headers';
import { listPosts } from '@/graphql/queries';
import awsExports from '@/aws-exports';
Amplify.configure({ ...awsExports, ssr: true });
export default async function Page() {
// Construct a req object & prepare an SSR enabled version of Amplify
const req = {
headers: {
cookie: headers().get('cookie'),
},
};
const SSR = withSSRContext({ req })
const { data } = await SSR.API.graphql({ query: listPosts });
// ...
}
```
## DataStore
### Serializing
For Next.js, returned `props` from the server have to be valid JSON. Because `DataStore.query(Model)` returns _instances_ of `Model`, you need the `serializeModel` helper to convert it to JSON instead:
```js
import { serializeModel } from '@aws-amplify/datastore/ssr';
import { Amplify, withSSRContext } from "aws-amplify";
...
export async function getServerSideProps({ req }) {
const SSR = withSSRContext({ req });
const posts = await SSR.DataStore.query(Post);
return {
props: {
// This converts Post instances into serialized JSON for the client
posts: serializeModel(posts),
},
};
}
```
### Deserializing
If your client-side code only reads from the server-side props and doesn't perform any updates to these models, then your client-side code won't need any changes.
However, if you receive models from the server and need to `DataStore.delete(model)` or `DataStore.save(...)` changes to them, you'll need the `deserializeModel` utility to convert them from server-friendly JSON back into model _instances_:
```js
import { deserializeModel } from '@aws-amplify/datastore/ssr';
import { Amplify, withSSRContext } from "aws-amplify";
import { Post } from "../src/models";
export default function HomePage(initialData) {
// This converts the serialized JSON back into Post instances
const [posts, setPosts] = useState(deserializeModel(Post, initialData.posts));
...
}
```
### Example:
Using the following schema:
```graphql
type Post
@model
@auth(rules: [{ allow: owner }, { allow: public, operations: [read] }]) {
id: ID!
title: String!
content: String!
}
```
Open **pages/index.js** and replace it with the following code:
```jsx
// pages/index.js
import Head from "next/head";
import styles from "../styles/Home.module.css";
import {
Amplify,
Auth,
AuthModeStrategyType,
DataStore,
withSSRContext,
} from "aws-amplify";
import { Authenticator } from "@aws-amplify/ui-react";
import { Post } from "../src/models";
import { serializeModel } from "@aws-amplify/datastore/ssr";
import awsExports from "../src/aws-exports";
Amplify.configure({
...awsExports,
DataStore: {
authModeStrategyType: AuthModeStrategyType.MULTI_AUTH,
},
ssr: true,
});
export async function getServerSideProps({ req }) {
const SSR = withSSRContext({ req });
const posts = await SSR.DataStore.query(Post);
return {
props: {
// This converts Post instances into serialized JSON for the client
posts: serializeModel(posts),
},
};
}
async function handleCreatePost(event) {
event.preventDefault();
const form = new FormData(event.target);
try {
const post = await DataStore.save(
new Post({
title: form.get("title"),
content: form.get("content"),
})
);
window.location.href = `/posts/${post.id}`;
} catch (error) {
console.log(error);
}
}
export default function Home({ posts = [] }) {
return (