Remix
Get started using Pinata with Remix
Startup Remix Project
We can start a Remix app by entering the command below into the terminal
npx create-remix@latest pinata-remix
After it’s finished installed we’ll want to cd
into the project and install the pinata
SDK.
cd pinata-remix && npm i pinata
Now let’s make a file called .env
file in the root of the project with the following variables:
PINATA_JWT= # The Pinata JWT API key we got earlier
GATEWAY_URL= # The Gateway domain we grabbed earlier, formatting just as we copied it from the app
Setup Pinata
Make a folder in the root of the project called utils
, and inside make a file called pinata.ts
with the following code:
import { PinataSDK } from "pinata";
export const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT,
pinataGateway: process.env.GATEWAY_URL,
});
This will create an instance of the SDK we can use throughout the app.
Upload Form and Server Action
In the app/routes
directory we can remove the boilerplate code and replace it with the code below.
import type { ActionFunctionArgs, MetaFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Form, useActionData, useNavigation } from "@remix-run/react";
import { pinata } from "utils/config";
export const meta: MetaFunction = () => {
return [
{ title: "Remix + Pinata" },
{ name: "description", content: "Upload files on Remix with Pinata!" },
];
};
// Server side action to handle our upload
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData();
const file = formData.get("file") as File;
const { cid } = await pinata.upload.file(file);
const url = await pinata.gateways.createSignedURL({
cid: cid,
expires: 60,
});
return json({ url });
};
export default function Index() {
const actionData = useActionData<typeof action>();
const navigation = useNavigation();
const isSubmitting = navigation.state === "submitting";
return (
<div className="font-sans p-4 flex flex-col gap-4 justify-center items-center min-h-screen max-w-[500px] mx-auto">
<h1 className="text-3xl font-bold">Remix + Pinata</h1>
<Form
encType="multipart/form-data"
method="post"
className="flex flex-col gap-4"
>
<input type="file" name="file" className="" />
<button
className="bg-[#582CD6] text-white rounded-md p-2"
type="submit"
>
{isSubmitting ? "Uploading..." : "Upload"}
</button>
</Form>
{actionData?.url && (
<div className="mt-4">
<a
href={actionData.url}
target="_blank"
rel="noreferrer"
className="text-[#582CD6] underline"
>
{actionData.url}
</a>
</div>
)}
</div>
);
}
Let’s walk through what’s happening here. To start we have our client side markup:
<div className="font-sans p-4 flex flex-col gap-4 justify-center items-center min-h-screen max-w-[500px] mx-auto">
<h1 className="text-3xl font-bold">Remix + Pinata</h1>
<Form
encType="multipart/form-data"
method="post"
className="flex flex-col gap-4"
>
<input type="file" name="file" className="" />
<button
className="bg-[#582CD6] text-white rounded-md p-2"
type="submit"
>
{isSubmitting ? "Uploading..." : "Upload"}
</button>
</Form>
{actionData?.url && (
<div className="mt-4">
<a
href={actionData.url}
target="_blank"
rel="noreferrer"
className="text-[#582CD6] underline"
>
{actionData.url}
</a>
</div>
)}
</div>
This is a simple Form
element provided by Remix which lets us make a submission to a server action. The most important piece to notice here is the encType="multipart/form-data"
, which is crucial to make file / multipart requests work in Remix!
Then we have our server action:
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData();
const file = formData.get("file") as File;
const { cid } = await pinata.upload.file(file);
const url = await pinata.gateways.createSignedURL({
cid: cid,
expires: 60,
});
return json({ url });
};
This will be the default endpoint used when we submit our form. It will parse the incoming formData
, get the file
attached, and then we upload it to Pinata using pinata.upload.file()
. From that method we deconstruct the result and return the cid
which we can use in createSignedURL
. This will make a URL we can return to the client and make accessible to the user, and in our case the <a>
tag that links to the content.