All You Need To Know About Blink and Actions!!
What are Actions!?
Actions are API endpoints or methods that perform specific tasks or operations in response to requests. They serve as the backend operations that interact with frontend components like Blinks.
Actions can be used to handle various operations such as data fetching, form submissions, or performing complex business logic. They provide the functionality that Blinks can utilize for dynamic interactions.
#How Actions API Looks
What are Blinks?
In simple terms, Blinks are like customizable, interactive buttons or visual elements on a website that can do things when you click or hover over them. Imagine them as smart, dynamic buttons that can change color, size, or animation based on what you want them to do.
Blinks are associated with Actions and crypto in the sense that they can be used to trigger specific operations or transactions in the world of cryptocurrency. For example, you might click a Blink to make a donation in crypto or to mint a new NFT. Actions, in this context, refer to the operations that Blinks can trigger, such as sending crypto, interacting with blockchain-based applications, or executing various commands. So, Blinks are essentially the interactive elements that make it easy for users to perform actions related to cryptocurrency and blockchain technologies.
#How Blinks GUI Actually Looks
Get Started#
To quickly get started with creating custom Solana Actions:
// Start: Create a nextjs project
npx create-next-app project-blink
cd project-blink
npm install @solana/actions
Creating a Basic Blink Component
In your pages directory, create a file named index.tsx (or index.js if you are not using TypeScript). Add the following code:
import { ActionGetResponse } from '@solana/actions';
// Define the ActionGetResponse interface
export interface ActionGetResponse {
// URL to the icon image
icon: string;
// Label for the Blink, typically a short text that describes the purpose or name of the Blink
label: string;
// Description of the Blink, providing more detailed information about what the Blink does
description: string;
// Title of the Blink, used as the main heading or title when displaying the Blink
title: string;
// Links related to the Blink, usually containing actions that users can perform
links: {
// An array of action objects, each representing a possible action the user can take
actions: Array<{
// URL for the action, indicating where the user will be directed or what API endpoint will be called
href: string;
// Label for the action button, describing what the action does
label: string;
// (Optional) Array of parameters for dynamic actions, allowing users to input specific data
parameters?: Array<{
// Name of the parameter, used as the key for the input value
name: string;
// Placeholder or label for the input field, guiding users on what to enter
label: string;
}>;
}>;
};
}
Integrating Blink with Actions
First, create a new file actionAdapter.ts in your project directory. Add the following code to handle Blink actions:
// actionAdapter.ts
import { ActionAdapter } from "@dialectlabs/blinks";
class MyActionAdapter implements ActionAdapter {
async signTransaction(tx: string) {
// Implement your signTransaction logic here
}
async connect() {
// Implement your connect logic here
}
async confirmTransaction(sig: string) {
// Implement your confirmTransaction logic here
}
}
export default MyActionAdapter;
Fetching Data from the API
Update your index.tsx file to fetch data from your API:
// pages/index.tsx
import { useEffect, useState } from 'react';
import { Blink } from "@dialectlabs/blinks";
import '@dialectlabs/blinks/index.css';
const HomePage = () => {
const [data, setData] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('/api/data');
const result = await response.json();
setData(result.message);
};
fetchData();
}, []);
return (
<div>
<h1>Welcome to My Blink App</h1>
<Blink stylePreset="default" />
<p>API Response: {data}</p>
</div>
);
};
export default HomePage;
Basic Usage
To get started with Blink, you need to install the library and import it into your components. Below is a simple example of how to use Blink in a React component.
// pages/api/blink.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { ActionGetResponse, ACTIONS_CORS_HEADERS } from '@solana/actions';
// GET request handler for Blink metadata
const handler = (req: NextApiRequest, res: NextApiResponse) => {
// Define the response payload according to the ActionGetResponse interface
const payload: ActionGetResponse = {
icon: , // URL to the icon image
label: "Support my project", // Label for the Blink
description: "Support my project with SOL using this blink!", // Description of the Blink
title: "Support Project - SOL Donation", // Title of the Blink
links: {
actions: [
{
href: "/api/actions/donate?amount=0.1", // URL for the action
label: "0.1 SOL", // Label for the action button
},
{
href: "/api/actions/donate?amount=0.5", // URL for the action
label: "0.5 SOL", // Label for the action button
},
{
href: "/api/actions/donate?amount=1.0", // URL for the action
label: "1.0 SOL", // Label for the action button
},
{
href: "/api/actions/donate?amount={amount}", // URL with a dynamic parameter
label: "Custom Amount", // Label for the action button
parameters: [
{
name: "amount", // Name of the parameter
label: "Enter SOL amount", // Placeholder or label for the input field
},
],
},
],
},
};
// Return the JSON response with appropriate CORS headers
res.status(200).json(payload);
};
export default handler;
Donations on Blinks!!
One of the main use cases that blinks are used rightnow are in donations. And here is a example!
import {
ActionGetResponse,
ACTIONS_CORS_HEADERS,
ActionPostRequest,
createPostResponse,
ActionPostResponse,
} from "@solana/actions";
import {
clusterApiUrl,
Connection,
LAMPORTS_PER_SOL,
PublicKey,
SystemProgram,
Transaction,
} from "@solana/web3.js";
export const GET = async (req: Request) => {
const payload: ActionGetResponse = {
icon: new URL("/img/nick.jpg", new URL(req.url).origin).toString(),
label: "Buy me a coffee",
description:
"Buy me a coffee with SOL using this super sweet blink of mine :)",
title: "Nick Frostbutter - Buy Me a Coffee",
links: {
actions: [
{
href: "/api/actions/donate?amount=0.1",
label: "0.1 SOL",
},
{
href: "/api/actions/donate?amount=0.5",
label: "0.5 SOL",
},
{
href: "/api/actions/donate?amount=1.0",
label: "1.0 SOL",
},
{
href: "/api/actions/donate?amount={amount}",
label: "Send SOL", // button text
parameters: [
{
name: "amount", // name template literal
label: "Enter a SOL amount", // placeholder for the input
},
],
},
],
},
};
return Response.json(payload, {
headers: ACTIONS_CORS_HEADERS,
});
};
export const OPTIONS = GET;
export const POST = async (req: Request) => {
try {
const url = new URL(req.url);
const body: ActionPostRequest = await req.json();
let account: PublicKey;
try {
account = new PublicKey(body.account);
} catch (err) {
throw "Invalid 'account' provided. Its not a real pubkey";
}
let amount: number = 0.1;
if (url.searchParams.has("amount")) {
try {
amount = parseFloat(url.searchParams.get("amount") || "0.1") || amount;
} catch (err) {
throw "Invalid 'amount' input";
}
}
const connection = new Connection(clusterApiUrl("devnet"));
const TO_PUBKEY = new PublicKey(
"9FK3BZiGatVrDwVZoMZsJQW24ETAmmzBAGPnJp9jSdtu",
);
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: account,
lamports: amount * LAMPORTS_PER_SOL,
toPubkey: TO_PUBKEY,
}),
);
transaction.feePayer = account;
transaction.recentBlockhash = (
await connection.getLatestBlockhash()
).blockhash;
const payload: ActionPostResponse = await createPostResponse({
fields: {
transaction,
message: "Thanks for the coffee fren :)",
},
});
return Response.json(payload, {
headers: ACTIONS_CORS_HEADERS,
});
} catch (err) {
let message = "An unknown error occurred";
if (typeof err == "string") message = err;
return Response.json(
{
message,
},
{
headers: ACTIONS_CORS_HEADERS,
},
);
}
};
#Testing Your Blink!!
When deploying your custom Solana Actions to production, it is essential to ensure your application has a valid actions.json file at the root of your domain. This file defines the available actions and their configurations. Additionally, make sure your application responds with the required Cross-Origin headers on all Action endpoints, including the actions.json file. These headers are necessary for enabling cross-origin requests, which are crucial for the proper functioning of your application.
To test and debug your Blinks and Actions, use the Blink Inspector. This tool helps you verify that your Blinks and Actions are set up correctly and functioning as expected. If you are looking for inspiration around building Actions and Blinks, check out the Awesome Blinks repository for some community creations and ideas for new ones. This repository showcases various examples and innovative uses of Blinks and Actions, providing a valuable resource for developers.
Advanced Configuration
An advanced configuration for the ActionGetResponse interface can include additional features like conditional actions, action grouping, metadata, and multi-language support
import { ActionGetResponse } from '@solana/actions';
// Define the advanced ActionGetResponse interface
export interface AdvancedActionGetResponse extends ActionGetResponse {
// Additional metadata about the Blink
metadata?: {
creator: string; // Creator of the Blink
createdAt: string; // Timestamp of when the Blink was created
};
// Support for multiple languages
translations?: {
[languageCode: string]: {
label: string; // Translated label
description: string; // Translated description
title: string; // Translated title
};
};
// Grouping related actions together
actionGroups?: Array<{
groupName: string; // Name of the action group
actions: Array<{
href: string;
label: string;
parameters?: Array<{
name: string;
label: string;
}>;
condition?: {
// Condition that determines whether the action should be shown
key: string; // Key to check in the context
value: any; // Value that the key should have
};
}>;
}>;
}
// Example usage in an API route
export const handler = (req: NextApiRequest, res: NextApiResponse) => {
// Define the response payload according to the AdvancedActionGetResponse interface
const payload: AdvancedActionGetResponse = {
icon: //image url,
label: "Support my project",
description: "Support my project with SOL using this blink!",
title: "Support Project - SOL Donation",
metadata: {
creator: "Your Name",
createdAt: new Date().toISOString(),
},
translations: {
es: {
label: "Apoya mi proyecto",
description: "¡Apoya mi proyecto con SOL usando este blink!",
title: "Apoyar Proyecto - Donación de SOL",
},
},
actionGroups: [
{
groupName: "Donate",
actions: [
{
href: "/api/actions/donate?amount=0.1",
label: "0.1 SOL",
},
{
href: "/api/actions/donate?amount=0.5",
label: "0.5 SOL",
},
{
href: "/api/actions/donate?amount=1.0",
label: "1.0 SOL",
},
{
href: "/api/actions/donate?amount={amount}",
label: "Custom Amount",
parameters: [
{
name: "amount",
label: "Enter SOL amount",
},
],
condition: {
key: "userRole",
value: "supporter", // Only show this action if the user role is 'supporter'
},
},
],
},
],
};
// Return the JSON response with appropriate CORS headers
res.status(200).json(payload);
};
export default handler;
Blink with Animation
Adding animations to Blinks can enhance the user experience by providing visual feedback. This example demonstrates how to use Blink with animation.
// Example use case 3: Blink with Animation
import { Blink } from 'blink-library';
import { useEffect, useState } from 'react';
function App() {
const [animate, setAnimate] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setAnimate(true), 1000);
return () => clearTimeout(timer);
}, []);
return (
<div>
<Blink className={animate ? 'animate-pulse' : ''}>Blinked with Animation!</Blink>
</div>
);
}
Conditional Blink Rendering
You can control the visibility of Blinks based on conditions. Here’s an example of toggling Blink visibility with user interaction.
// Example use case 4: Conditional Blink Rendering
import { Blink } from 'blink-library';
function App() {
const [isVisible, setIsVisible] = useState(true);
const toggleVisibility = () => setIsVisible(prev => !prev);
return (
<div>
<button onClick={toggleVisibility}>Toggle Blink</button>
{isVisible && <Blink>Conditional Blink Visibility</Blink>}
</div>
);
}
Responsive Blink Styling
To create a responsive Blink component with proper styling, we can use Tailwind CSS along with React. This snippet will include a responsive layout for the Blink component, ensuring it looks good on various screen sizes.
// components/BlinkComponent.tsx
import { useEffect, useState } from 'react';
interface ActionLink {
href: string;
label: string;
parameters?: Array<{
name: string;
label: string;
}>;
}
interface ActionGetResponse {
icon: string;
label: string;
description: string;
title: string;
links: {
actions: ActionLink[];
};
}
const BlinkComponent = () => {
const [blinkData, setBlinkData] = useState<ActionGetResponse | null>(null);
// Fetch Blink metadata from the API
useEffect(() => {
fetch('/api/blink')
.then(response => response.json())
.then(data => setBlinkData(data))
.catch(error => console.error('Error fetching Blink metadata:', error));
}, []);
if (!blinkData) {
return <div>Loading...</div>;
}
return (
<div className="p-4 max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
<div className="md:flex">
<div className="md:shrink-0">
<img className="h-48 w-full object-cover md:h-full md:w-48" src={blinkData.icon} alt={blinkData.label} />
</div>
<div className="p-8">
<div className="uppercase tracking-wide text-sm text-indigo-500 font-semibold">{blinkData.title}</div>
<p className="mt-2 text-gray-500">{blinkData.description}</p>
<div className="mt-4">
{blinkData.links.actions.map((action, index) => (
<a key={index} href={action.href} className="block mt-1 text-lg leading-tight font-medium text-black hover:underline">
{action.label}
</a>
))}
</div>
</div>
</div>
</div>
);
};
export default BlinkComponent;
#Blinks on Twitter
Blinks on Solana are revolutionizing how blockchain functionality is integrated into web experiences. Here are some exciting ways Blinks are being used, including on Twitter:
- Donations and Tips: Content creators can share a DONATE TO ME Blink on Twitter. When users click the link, a donation widget appears directly in the tweet, allowing fans to tip the creator in SOL instantly.
- NFT Minting and Governance: Blinks enable minting custom NFTs or participating in governance votes directly from URLs. For instance, a community could vote on policies via links shared in newsletters or social media posts.
- Seamless Transactions: Users can initiate transactions by clicking Blinks, adding tokens to their Solana wallets without complex setups. These Blinks can be shared across platforms, including Twitter.
- Simplified Interactions: Blinks allow users to perform blockchain-related actions, such as making cryptocurrency donations or minting NFTs, directly from tweets. This integration makes it easier for users to engage with blockchain technologies without leaving the Twitter platform.
#Crypto In Crypto Twitter!!
#Explore Resources:
For additional examples and resources on Blink and Actions, explore the following:
- GitHub Repository - Check out the codebase for Blink and Actions.
- Understanding Blinks and Actions - Comprehensive guide on Blink’s features and usage.
- Actions and Blinks Documentation - Detailed documentation on implementing and using Actions and Blinks.