migrate.mdโข11.4 kB
# Migrate an Existing App
> Quickly migrate your existing app to a mini app, preview it in Base Build, and publish to the Base app.
<Panel>
<iframe className="w-3/4 aspect-video rounded-xl mx-auto block" src="https://www.youtube-nocookie.com/embed/bChuIvqLiX0" title="Turning an Existing App into a Mini App on the Base App" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />
</Panel>
**Prerequisites**
* You have an existing web app
* You have a Base app account
<Steps>
<Step title="Add the MiniApp SDK">
<CodeGroup>
```bash npm
npm install @farcaster/miniapp-sdk
```
```bash pnpm
pnpm add @farcaster/miniapp-sdk
```
```bash yarn
yarn add @farcaster/miniapp-sdk
```
</CodeGroup>
</Step>
<Step title="Trigger App Display">
Once your app has loaded, call `sdk.actions.ready()` to hide the loading splash screen and display your app.
<Tabs>
<Tab title="Vanilla JS">
```javascript app.js
import { sdk } from '@farcaster/miniapp-sdk';
// Once app is ready to be displayed
await sdk.actions.ready();
```
</Tab>
<Tab title="React">
In React apps, call `ready()` inside a `useEffect` hook to prevent it from running on every re-render. Call `ready()` as soon as possible and avoid jitter and content reflows.
```typescript app.tsx
import { sdk } from '@farcaster/miniapp-sdk';
import { useEffect } from 'react';
function App() {
useEffect(() => {
sdk.actions.ready();
}, []);
return(...your app content goes here...)
}
export default App;
```
</Tab>
</Tabs>
</Step>
<Step title="Host the Manifest">
Create a file available at `https://www.your-domain.com/.well-known/farcaster.json`.
<Tabs>
<Tab title="Vanilla JS">
Create the manifest file in your project at `/public/.well-known/farcaster.json`.
</Tab>
<Tab title="Next.js">
Create a Next.js rouote to host your manifest file
```typescript app/.well-known/farcaster.json/route.ts
function withValidProperties(properties: Record<string, undefined | string | string[]>) {
return Object.fromEntries(
Object.entries(properties).filter(([_, value]) => (Array.isArray(value) ? value.length > 0 : !!value))
);
}
export async function GET() {
const URL = process.env.NEXT_PUBLIC_URL as string;
return Response.json(paste_manifest_json_object_here); // see the next step for the manifest_json_object
}
```
</Tab>
</Tabs>
</Step>
<Step title="Update the Manifest">
Copy the example manifest below and add it to the file created in the previous step. Update each field in the `miniapp`.
For details on each field, see the [field reference](/mini-apps/features/manifest#field-reference)
### Example Manifest
```json /.well-known/farcaster.json
{
"accountAssociation": { // these will be added in step 5
"header": "",
"payload": "",
"signature": ""
},
"baseBuilder": {
"allowedAddresses": [""] // add your Base Account address here
},
"miniapp": {
"version": "1",
"name": "Example Mini App",
"homeUrl": "https://ex.co",
"iconUrl": "https://ex.co/i.png",
"splashImageUrl": "https://ex.co/l.png",
"splashBackgroundColor": "#000000",
"webhookUrl": "https://ex.co/api/webhook",
"subtitle": "Fast, fun, social",
"description": "A fast, fun way to challenge friends in real time.",
"screenshotUrls": [
"https://ex.co/s1.png",
"https://ex.co/s2.png",
"https://ex.co/s3.png"
],
"primaryCategory": "social",
"tags": ["example", "miniapp", "baseapp"],
"heroImageUrl": "https://ex.co/og.png",
"tagline": "Play instantly",
"ogTitle": "Example Mini App",
"ogDescription": "Challenge friends in real time.",
"ogImageUrl": "https://ex.co/og.png",
"noindex": true
}
}
```
</Step>
<Step title="Create accountAssociation Credentials">
The `accountAssociation` fields in the manifest are used to verify ownership of your app. You can generate these fields on Base Build.
1. Ensure all changes are live so that the Manifest file is available at your app's url.
2. Navigate to the Base Build [Account association tool](https://www.base.dev/preview?tab=account).
3. Paste your domain in the `App URL` field (ex: sample-url.vercel.app) and click "Submit"
4. Click on the "Verify" button that appears and follow the instructions to generate the `accountAssociation` fields.
5. Copy the `accountAssociation` fields and paste them into the manifest file you added in the previous step.
```json /.well-known/farcaster.json
{
"accountAssociation": {
"header": "eyJmaWQiOjkxNTIsInR5cGUiOiJjdXN0b2R5Iiwia2V5IjoiMHgwMmVmNzkwRGQ3OTkzQTM1ZkQ4NDdDMDUzRURkQUU5NDBEMDU1NTk2In0",
"payload": "eyJkb21haW4iOiJhcHAuZXhhbXBsZS5jb20ifQ",
"signature": "MHgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxNzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEyNDdhNDhlZGJmMTMwZDU0MmIzMWQzZTg1ZDUyOTAwMmEwNDNkMjM5NjZiNWVjNTNmYjhlNzUzZmIyYzc1MWFmNTI4MWFiYTgxY2I5ZDE3NDAyY2YxMzQxOGI2MTcwYzFiODY3OTExZDkxN2UxMzU3MmVkMWIwYzNkYzEyM2Q1ODAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjVmMTk4MDg2YjJkYjE3MjU2NzMxYmM0NTY2NzNiOTZiY2VmMjNmNTFkMWZiYWNkZDdjNDM3OWVmNjU0NjU1NzJmMWQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwOGE3YjIyNzQ3OTcwNjUyMjNhMjI3NzY1NjI2MTc1NzQ2ODZlMmU2NzY1NzQyMjJjMjI2MzY4NjE2YzZjNjU2ZTY3NjUyMjNhMjI2NDJkMzQ0YjMzMzMzNjUyNDY3MDc0MzE0NTYxNjQ2Yjc1NTE0ODU3NDg2ZDc5Mzc1Mzc1Njk2YjQ0MzI0ZjM1NGE2MzRhNjM2YjVhNGM3NDUzMzczODIyMmMyMjZmNzI2OTY3Njk2ZTIyM2EyMjY4NzQ3NDcwNzMzYTJmMmY2YjY1Nzk3MzJlNjM2ZjY5NmU2MjYxNzM2NTJlNjM2ZjZkMjIyYzIyNjM3MjZmNzM3MzRmNzI2OTY3Njk2ZTIyM2E2NjYxNmM3MzY1N2QwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMA"
},
"miniapp": {...} // these fields remain the same
}
```
<Info>
Note: Because you are signing with your Base Account, the `signature` field will be significantly longer than if you were to sign directly with your Farcaster custody wallet.
</Info>
</Step>
<Step title="Add Embed Metadata">
Update your index.html file to include the `fc:miniapp` metadata. This is used to generate the rich embeds when your app is shared and is required for your app to display.
<Tabs>
<Tab title="Vanilla JS">
Add directly to your index.html file.
```html index.html
<meta name="fc:miniapp" content='{
"version":"next",
"imageUrl":"https://your-app.com/embed-image",
"button":{
"title":"Play Now",
"action":{
"type":"launch_miniapp",
"name":"Your App Name",
"url":"https://your-app.com"
}
}
}' />
```
</Tab>
<Tab title="Next.js">
Use the `generateMetadata` function to add the `fc:miniapp` metadata.
```typescript app/layout.tsx
export async function generateMetadata(): Promise<Metadata> {
return {
other: {
'fc:miniapp': JSON.stringify({
version: 'next',
imageUrl: 'https://your-app.com/embed-image',
button: {
title: `Launch Your App Name`,
action: {
type: 'launch_miniapp',
name: 'Your App Name',
url: 'https://your-app.com',
splashImageUrl: 'https://your-app.com/splash-image',
splashBackgroundColor: '#000000',
},
},
}),
},
};
}
```
</Tab>
</Tabs>
</Step>
<Step title="Push to Production">
Ensure all changes are live.
</Step>
<Step title="Preview Your App">
Use the Base Build [Preview tool](https://www.base.dev/preview) to validate your app.
1. Add your app URL to view the embeds and click the launch button to verify the app launches as expected.
2. Use the "Account association" tab to verify the association credentials were created correctly.
3. Use the "Metadata" to see the metadata added from the manifest and identify any missing fields.
<video autoPlay muted loop playsInline src="https://mintcdn.com/base-a060aa97/hlNNNlUJtlshvXQM/videos/mini-apps/basebuildpreview.mp4?fit=max&auto=format&n=hlNNNlUJtlshvXQM&q=85&s=65a4cb8ce13c9940cba6aee73b8ececb" data-path="videos/mini-apps/basebuildpreview.mp4" />
</Step>
<Step title="Post to Publish">
To publish your app, create a post in the Base app with your app's URL.
<img src="https://mintcdn.com/base-a060aa97/t8Sjfqig2G4AU7Gh/images/minikit/publish-app-base.png?fit=max&auto=format&n=t8Sjfqig2G4AU7Gh&q=85&s=71a07b27f04a4df65f47fced5b2b76a5" alt="Posting an app to Base app" height="300" className="rounded-lg" data-og-width="1143" data-og-height="1380" data-path="images/minikit/publish-app-base.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/base-a060aa97/t8Sjfqig2G4AU7Gh/images/minikit/publish-app-base.png?w=280&fit=max&auto=format&n=t8Sjfqig2G4AU7Gh&q=85&s=aa2a25afd0e22fad807642a6753446fc 280w, https://mintcdn.com/base-a060aa97/t8Sjfqig2G4AU7Gh/images/minikit/publish-app-base.png?w=560&fit=max&auto=format&n=t8Sjfqig2G4AU7Gh&q=85&s=187a5bdceb902dbfb0714088301bb58e 560w, https://mintcdn.com/base-a060aa97/t8Sjfqig2G4AU7Gh/images/minikit/publish-app-base.png?w=840&fit=max&auto=format&n=t8Sjfqig2G4AU7Gh&q=85&s=8e731221f349c80283e57ee3fddd5827 840w, https://mintcdn.com/base-a060aa97/t8Sjfqig2G4AU7Gh/images/minikit/publish-app-base.png?w=1100&fit=max&auto=format&n=t8Sjfqig2G4AU7Gh&q=85&s=fa5af302bc79f138a4989c91fb5f4c6b 1100w, https://mintcdn.com/base-a060aa97/t8Sjfqig2G4AU7Gh/images/minikit/publish-app-base.png?w=1650&fit=max&auto=format&n=t8Sjfqig2G4AU7Gh&q=85&s=90bbb4dbaea6ce60b0fc145348888ded 1650w, https://mintcdn.com/base-a060aa97/t8Sjfqig2G4AU7Gh/images/minikit/publish-app-base.png?w=2500&fit=max&auto=format&n=t8Sjfqig2G4AU7Gh&q=85&s=66414a12494828300cad19fef435b18c 2500w" />
</Step>
</Steps>