ArNext is a NextJS-based framework that lets you deploy the same codebase both on Vercel and Arweave.
This allows permaapps to have all kinds of cloud-powered performance optimizations, which were not possible before.
Permaapps are SPAs (single-page applications) deployed on Arweave, which require complete SSG (static site generation) to build the app before deployment. Permaapps traditionally allow only client-side hash routing within the SPA due to the lack of server-side routing.
An example permaapp.
There are a number of limitations permaapps introduce.
- Loading of dynamic content is very slow due to the lack of SSR (server-side rendering).
- Only the root URL is accessible with client-side hash routing. Regular browser routing without the hash will take you to inaccessible pages when reloading.
- Social media cards don't work for dynamically generated pages. You can have only one card for the root page because dynamically generated meta tags with Javascript are not read by social apps such as X (ex-Twitter).
- No server-side optimizations such as SSR, ISR, and edge CDN are possible.
With these limitations, permaapps cannot practically onboard millions of users.
An ArNext app is fundamentally a NextJS app, but it also allows the building of an identical permaapp from the same codebase through a series of hacks. It's a multi-page SPA (!?) with SSR on Vercel and client-side routing on Arweave.
- You can deploy the identical apps on Vercel and Arweave.
- The permaapps deployed on Arweave function as a censorship-resistant permanent backup.
- The app deployed on Vercel can have all kinds of cloud-powered optimizations.
- The app works as a statically generated multi-page site, but once a page is loaded, it works as a SPA with client-side routing.
Now compare the performance of the permaapp deployed on Vercel below with that of the one deployed on Arweave above.
The one on Vercel is extremely fast with ISR (incremental static regeneration).
Also, the open graph meta tags for dynamically generated pages are enabled with SSR.
Hash routing is no longer required on Arweave-deployed permaapps.
This was enabled by utilizing fallback
of the Arweave Manifests v0.2.0.
ArNext generates optimized manifests with fallback
with a modified version of arkb.
The biggest challenge to building a static permaapp from the NextJS codebase is the static file linking. Arweave gateways deploy permaapps with a subdirectory URL, and you cannot know the subdirectory name before building the app because it's the hash of the built files. NextJS allows basePath
to build the app for subdirectory deployment, but you cannot use that if you don't know the hash before the build. And basePath
only allows absolute paths. So we cannot use that.
You need to dynamically calculate and insert relative paths for assets after the app is loaded on a URL, but NextJS doesn't allow such things. In fact, no major web frameworks allow such file linking, for that matter.
There are 3 hacks combined to solve this issue.
- Dynamically calculate the relative path and insert tags in the HTML head (_document.js)
- Manually rewrite asset tags after building the app code, delete unnecessary html files, and modify the webpack generated js file to produce correct paths during runtime (arweave.mjs)
- Generate a proper
manifest.json
with modified arkb before deployment (deploy.js)
npx create-arnext-app myapp
cd myapp && yarn
yarn dev
vercel --prod
yarn arweave
Now, the static app to deploy on Arweave is running on localhost:3000.
yarn deploy -w WALLET_PATH
Deploying using Turbo requires a wallet funded with Turbo Credits
yarn deploy:turbo -w WALLET_PATH
A JSON object will be printed to the console with upload information for each file, the arweave manifest generated, and the upload response from uploading the manifest. The manifest id for your deployment can be found at manifestResponse.id
.
There are a couple of minor changes to make in your NextJS app code to make it work the same on both Vercel and Arweave.
Currently, ArNext only works with the NextJS page router and react-router-dom
for Arweave.
Replace Link
, useParams
, and useRouter
with the ones from arnext
. It will align next/router
and react-router-dom
.
import { Link, useParams, useRouter } from "arnext"
export default function Post() {
const router = useRouter() // router.push(pathname)
const { id } = useParams()
...
return <Link href={`/post/${id}`}>/post/{id}</Link>
}
Wrap getStaticProps
with ssr
.
import { ssr } from "arnext"
export const getStaticProps = ssr(async ({}) => {
...
return { props }
})
Currently, ArNext only works with the page router without src
directory and JS.
If you have an existing NextJS app, follow the steps below. Using create-arnext-app
and manually copying the source code might be less hassle.
- Remove unsupported features
ArNext doesn't support certain NextJS features at the moment. If your app is using any of the following features, find alternative ways to implement them.
- Typescript
- App Router
src
directorynext/font/local
next/image
next/router
only supportspush
They will be supported in future releases.
- Install necessary dependencies.
yarn add arnext
yarn add arnext-arkb cheerio cross-env starknet @ardrive/turbo-sdk --dev
- Wrap config in
next.config.mjs
.
/** @type {import('next').NextConfig} */
import arnext from "arnext/config"
const nextConfig = { reactStrictMode: true }
export default arnext(nextConfig)
- Replace
Head
in_document.js
.
import { Html, Main, NextScript } from "next/document"
import { Head } from "arnext"
export default function Document() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
- Replace
Component
withArNext
in_app.js
.
import { ArNext } from "arnext"
export default function App(props) {
return <ArNext {...props} />
}
- Wrap
getStaticProps
withssr
import { ssr } from "arnext"
export const getStaticProps = ssr(async ({}) => {
...
return { props }
})
- Replace
Link
,useParams
,useRouter
with the ones fromarnext
.
import { Link, useParams, useRouter } from "arnext"
export default function Post() {
const router = useRouter() // router.push(pathname)
const { id } = useParams()
...
return <Link href={`/post/${id}`}>/post/{id}</Link>
}
-
Copy
arweave.mjs
file to the app root directory. -
Add script commands to
package.json
.
{
...
"scripts": {
"arweave": "npm run build:arweave && npx serve -s out",
"deploy": "node node_modules/arnext-arkb deploy out",
"deploy:turbo": "turbo upload-folder --folder-path out",
"build:arweave": "cross-env NEXT_PUBLIC_DEPLOY_TARGET='arweave' next build && node arweave.mjs",
...
},
...
}
- Build for Arweave
yarn arweave
The static version of the NextJS app to be deployed on Arweave should be running at localhost:3000.
- Deploy on Arweave
If everything is working, you can deploy the app either using arkb
or turbo
.
yarn deploy -w KEYFILE
yarn deploy:turbo -w KEYFILE