How to Generate GitHub-Style Dynamic OG:Images with Flyyer
Flyyer lets you create GitHub-style social media link previews with dynamic Open Graph (OG) images using React, TailwindCSS, and URL variables.
By Patricio Lopez Juri
Why Do GitHub’s Link Previews Stand Out?
GitHub generates dynamic cards for every repository link. These previews:
- Include the repo name, description, and avatar
- Show real-time stats (stars, forks, etc.)
- Look clean and consistent across platforms
Flyyer allows you to replicate this functionality—without building a custom image service.
📌 Note: GitHub uses its own infrastructure. This example is not affiliated with GitHub.
What Will You Build?
You’ll create a Flyyer template to dynamically render repository previews like GitHub’s cards.
Preview formats supported:
| Format | Dimensions |
| Thumbnail | 400×400 px |
| Banner | 1200×630 px (OG) |
| Square | 1080×1080 px |
| Story | 1080×1920 px |
🎯 Live Demo:
Try editing this preview URL
How to Get Started
Use the create-flyyer-app CLI to scaffold a new project.
bash
CopyEdit
# Using npm
npm init flyyer-app@latest github-cards
# Using Yarn
yarn create flyyer-app github-cards
📦 Choose: react-typescript-tailwind
This starter uses React, TypeScript, and Tailwind—just like GitHub’s frontend stack.
Project Setup
bash
CopyEdit
cd github-cards
npm install
npm start
This opens Flyyer Studio at useflyyer.github.io/studio/.
How to Build the Template
Edit the template:
bash
CopyEdit
mv templates/article.tsx templates/repository.tsx
Open repository.tsx and structure it with:
- Repo name
- Avatar image
- Description
- Contributor stats
Flyyer reads dynamic content from the querystring in the URL.
🔧 Tailwind Tweak
Update styles/tailwind.css to set a larger base font size:
css
CopyEdit
html {
font-size: 36px;
}
This improves sizing in image-based previews.
Example Code: GitHub Repository Template
tsx
CopyEdit
export default function RepositoryTemplate(props: TemplateProps) {
const {width, height, variables} = props;
const [owner, repo] = (variables.title || ”).split(‘/’);
const stats = [
{Icon: VscOrganization, title: ‘Contributors’, count: variables.contributors},
{Icon: VscIssues, title: ‘Issues’, count: variables.issues},
{Icon: VscStarEmpty, title: ‘Stars’, count: variables.stars},
{Icon: VscRepoForked, title: ‘Forks’, count: variables.forks}
];
return (
<Layer className=”bg-white px-7 pt-10 pb-8 grid grid-cols-12 grid-rows-12 gap-x-5 text-gray-500″>
<header className=”col-span-9 row-span-10″>
<h1 className=”text-3xl tracking-normal text-gray-800″>
<span>{owner}</span>{owner && repo && <span>/</span>}
<span className=”font-bold”>{repo}</span>
</h1>
<p className=”pt-3 text-base font-light leading-snug”>{variables.description}</p>
</header>
<div className=”col-span-3 row-span-10″>
{variables.avatar && (
<img src={variables.avatar} className=”rounded-md w-full object-contain” />
)}
</div>
<dl className=”col-span-12 row-span-2 flex flex-wrap space-x-6″>
{stats.map(({title, count, Icon}, i) => (
<div key={i} className=”flex space-x-1″>
<Icon className=”w-4 h-4″ />
<div>
<dt className=”text-sm text-gray-700″>{Number.isFinite(count) ? count : ‘-‘}</dt>
<dd className=”text-xs text-gray-400″>{title}</dd>
</div>
</div>
))}
</dl>
</Layer>
);
}
📌 Use the Flyyer Studio Variables UI to test your image output.
How to Add Responsive Breakpoints in Tailwind
Flyyer templates have unique dimensions. Extend Tailwind breakpoints in tailwind.config.js:
js
CopyEdit
screens: {
thumb: {raw: ‘(min-height: 400px)’},
banner: {raw: ‘(min-height: 630px)’},
sq: {raw: ‘(min-height: 1080px)’},
story: {raw: ‘(min-height: 1920px)’},
…defaultTheme.screens
}
✅ Enables conditional styles for image sizes like story:text-4xl or sq:mt-4.
How to Deploy Your Template
bash
CopyEdit
NODE_ENV=production npm run-script build
npm run-script deploy
⚠️ Missing key?
- Visit flyyer.com/dashboard/_/settings
- Set your FLYYER_KEY in a .env file:
bash
CopyEdit
# .env
FLYYER_KEY=your-key-here
🔗 Deploy URLs Example:
bash
CopyEdit
How to Integrate With Next.js
Flyyer works by injecting dynamic URLs into Open Graph meta-tags.
tsx
CopyEdit
import Head from “next/head”
import { FlyyerRender } from “@flyyer/flyyer”
export function MyHead({ title, description, avatar, contributors, issues, forks, stars }) {
const flyyer = new FlyyerRender({
tenant: “flyyer”,
deck: “github-cards”,
template: “repository”,
variables: { title, description, avatar, contributors, issues, forks, stars }
});
return (
<Head>
<meta property=”og:image” content={flyyer.href()} />
<meta name=”twitter:image” content={flyyer.href()} />
<meta name=”twitter:card” content=”summary_large_image” />
</Head>
);
}
What’s Next?
In the next post, we’ll cover:
- How to declare variables with @flyyer/variables
- How to enable the Live Template UI on the Flyyer Dashboard
Helpful Links
- 📖 Flyyer Docs
- 🧠 Flyyer URL Formatters
- 🐦 Follow us on Twitter
- 💬 Join our Discord
