Vanilla JavaScript
Build Movement Mini Apps with pure JavaScript - no framework needed.
Prerequisites
- Basic HTML, CSS, and JavaScript knowledge
- Text editor (VS Code, Sublime, etc.)
- Local development server
Quick Start
1. Create Project Structure
bash
mkdir my-miniapp
cd my-miniappCreate these files:
my-miniapp/
├── index.html
├── style.css
└── app.js2. Basic HTML Setup
index.html:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Movement Mini App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>Movement Mini App</h1>
<div id="wallet-info" class="card">
<div id="connecting">
<p>Connecting to wallet...</p>
</div>
<div id="connected" style="display: none;">
<p>Connected Wallet:</p>
<p id="wallet-address" class="address"></p>
<button id="send-btn" class="btn">Send Transaction</button>
</div>
<div id="not-connected" style="display: none;">
<p>⚠️ This app must run inside Movement Everything</p>
</div>
</div>
<div id="status" class="status"></div>
</div>
<script src="app.js"></script>
</body>
</html>3. Add Styling
style.css:
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
}
h1 {
color: white;
text-align: center;
margin-bottom: 30px;
font-size: 2rem;
}
.card {
background: white;
border-radius: 16px;
padding: 24px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
.address {
font-family: 'Courier New', monospace;
font-size: 14px;
word-break: break-all;
background: #f5f5f5;
padding: 12px;
border-radius: 8px;
margin: 12px 0;
}
.btn {
width: 100%;
padding: 14px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.btn:hover {
background: #5568d3;
}
.btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.status {
background: white;
border-radius: 16px;
padding: 16px;
font-size: 14px;
display: none;
}
.status.show {
display: block;
}
.status.success {
background: #d4edda;
color: #155724;
}
.status.error {
background: #f8d7da;
color: #721c24;
}4. Implement SDK Integration
app.js:
javascript
// Check if Movement SDK is available
function checkSDK() {
return typeof window.movementSDK !== 'undefined';
}
// Show/hide UI elements
function showElement(id) {
document.getElementById(id).style.display = 'block';
}
function hideElement(id) {
document.getElementById(id).style.display = 'none';
}
// Show status message
function showStatus(message, type = 'info') {
const statusEl = document.getElementById('status');
statusEl.textContent = message;
statusEl.className = `status show ${type}`;
setTimeout(() => {
statusEl.classList.remove('show');
}, 5000);
}
// Initialize the app
async function init() {
if (!checkSDK()) {
hideElement('connecting');
showElement('not-connected');
return;
}
const sdk = window.movementSDK;
// Check if already connected
if (sdk.isConnected) {
onWalletConnected(sdk.address);
} else {
// Try to connect
try {
const account = await sdk.connect();
onWalletConnected(account.address);
} catch (error) {
console.error('Connection failed:', error);
showStatus('Failed to connect wallet', 'error');
}
}
}
// Handle successful wallet connection
function onWalletConnected(address) {
hideElement('connecting');
hideElement('not-connected');
showElement('connected');
document.getElementById('wallet-address').textContent = address;
// Setup button click handler
document.getElementById('send-btn').addEventListener('click', sendTransaction);
showStatus('Wallet connected successfully!', 'success');
}
// Send a transaction
async function sendTransaction() {
const sdk = window.movementSDK;
const btn = document.getElementById('send-btn');
if (!sdk || !sdk.isConnected) {
showStatus('Wallet not connected', 'error');
return;
}
try {
btn.disabled = true;
btn.textContent = 'Sending...';
// Example: Transfer 0.01 MOVE
const result = await sdk.sendTransaction({
function: '0x1::aptos_account::transfer',
arguments: [
'0xRECIPIENT_ADDRESS', // Replace with actual address
'1000000' // 0.01 MOVE
],
type_arguments: []
});
showStatus(`Transaction sent! Hash: ${result.hash}`, 'success');
} catch (error) {
console.error('Transaction error:', error);
showStatus(`Transaction failed: ${error.message}`, 'error');
} finally {
btn.disabled = false;
btn.textContent = 'Send Transaction';
}
}
// Start the app when page loads
document.addEventListener('DOMContentLoaded', init);Advanced Example: Token Balance Checker
javascript
async function getBalance() {
const sdk = window.movementSDK;
if (!sdk || !sdk.isConnected) {
return null;
}
try {
// Get APT coin balance
const response = await fetch(
`https://fullnode.mainnet.movementlabs.xyz/v1/accounts/${sdk.address}/resource/0x1::coin::CoinStore<0x1::aptos_coin::MovementCoin>`
);
const data = await response.json();
const balance = parseInt(data.data.coin.value) / 100000000; // Convert from Octas
return balance;
} catch (error) {
console.error('Failed to fetch balance:', error);
return null;
}
}
// Display balance
async function displayBalance() {
const balance = await getBalance();
if (balance !== null) {
const balanceEl = document.getElementById('balance');
balanceEl.textContent = `${balance.toFixed(4)} APT`;
}
}Example: Simple Game with Rewards
html
<!DOCTYPE html>
<html>
<head>
<title>Coin Clicker</title>
<style>
.game {
text-align: center;
padding: 40px;
}
.coin {
font-size: 120px;
cursor: pointer;
user-select: none;
}
.score {
font-size: 32px;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="game">
<h1>Coin Clicker</h1>
<p class="score">Score: <span id="score">0</span></p>
<div class="coin" id="coin">🪙</div>
<button id="claim-btn" style="display:none;">Claim Reward</button>
</div>
<script>
let score = 0;
const coinEl = document.getElementById('coin');
const scoreEl = document.getElementById('score');
const claimBtn = document.getElementById('claim-btn');
coinEl.addEventListener('click', () => {
score++;
scoreEl.textContent = score;
// Animate coin
coinEl.style.transform = 'scale(1.2)';
setTimeout(() => {
coinEl.style.transform = 'scale(1)';
}, 100);
// Show claim button at score 100
if (score === 100) {
claimBtn.style.display = 'block';
}
});
claimBtn.addEventListener('click', async () => {
if (window.movementSDK && window.movementSDK.isConnected) {
try {
await window.movementSDK.sendTransaction({
function: '0x1::aptos_account::transfer',
arguments: [
window.movementSDK.address,
'10000000' // 0.1 MOVE reward
],
type_arguments: []
});
alert('Reward claimed! 0.1 MOVE sent to your wallet');
score = 0;
scoreEl.textContent = '0';
claimBtn.style.display = 'none';
} catch (error) {
alert('Failed to claim reward: ' + error.message);
}
}
});
</script>
</body>
</html>Development
Run Locally
Use any local server:
bash
# Python
python3 -m http.server 3000
# Node.js
npx serve .
# PHP
php -S localhost:3000Test in Movement App
- Open Movement Everything
- Settings → Developer Mode → ON
- Mini App Testing section
- Enter
http://localhost:3000 - Click "Test App"
Deployment
Build
No build step needed! Just upload your files.
Deploy
GitHub Pages
bash
git init
git add .
git commit -m "Initial commit"
git branch -M gh-pages
git remote add origin https://github.com/username/repo.git
git push -u origin gh-pagesNetlify
bash
# Drag and drop your folder to netlify.com
# Or use CLI:
netlify deploy --prodVercel
bash
vercel deployTips
- Keep it simple: Vanilla JS apps load faster
- Mobile first: Design for touch interactions
- Test offline: Handle network errors gracefully
- Progressive enhancement: Work without SDK, enhance when available
Next Steps
- Explore the SDK API Reference
- Check out example projects
- Join Movement Discord
