Preparing Your Next.js Game
Next.js is a powerful React framework that can be used to build interactive web games. Our platform supports both static exports and server-side rendered Next.js applications.
Set Up Your Next.js Project
If you haven't already, create a new Next.js project:
Note: We recommend using yarn as your package manager for Next.js projects.
Configure Your Project
Update your next.config.js file based on your deployment strategy:
For Static Export (Recommended)
For Server-Side Rendering
Important: Static export is recommended for most games as it's simpler to deploy and has better performance for client-side interactive applications.
Build Your Game
Run the build command to create a production-ready version of your game:
For Static Export
This will create an out folder containing your static export with the following structure:
For Server-Side Rendering
This will create a .next folder containing your server-side application with the following structure:
Test Your Build Locally
Before uploading, test your built game to ensure everything works correctly:
For Static Export
For Server-Side Rendering
Check that all game features, assets, and interactions work as expected.
Zip Your Build
Compress your build folder into a ZIP file:
For Static Export
For Server-Side Rendering
Important: For static exports, make sure to zip the contents of the out folder, not the out folder itself.
Common Issues and Solutions
Image Optimization
If you're using Next.js Image component and having issues:
- Set
unoptimized: truein your next.config.js for static exports - Use regular
<img>tags for static assets - Place images in the
publicfolder for direct access
API Routes in Static Exports
API routes don't work in static exports. Instead:
- Use client-side API calls to external services
- Consider using serverless functions (like Vercel Edge Functions) for dynamic functionality
- Pre-compute data at build time using
getStaticProps
Routing Issues
If you're experiencing routing problems:
- Set
trailingSlash: truein your next.config.js - Use
Linkcomponents for internal navigation - Ensure all links use relative paths
Implementing Game Session Tracking
All games uploaded to our platform must implement session tracking functionality. This allows us to track player sessions and maintain leaderboards for your game.
Important: Your game bundle must include API calls to our session tracking endpoints. Our validation system will check for these calls during the upload process. Games without proper session tracking implementation will be rejected.
Option 1: Use Our SDK (Recommended)
The easiest way to implement session tracking is to use our JavaScript SDK. You can download the SDK here or view an example implementation.
In your Next.js project, you can add it in a few different ways:
Method A: Using Script Component
// In your _app.js or a game component
import Script from 'next/script';
import { useEffect, useState } from 'react';
export default function Game() {
const [tracker, setTracker] = useState(null);
useEffect(() => {
// Initialize tracker when the SDK is loaded
if (window.GameSessionTracker) {
setTracker(new window.GameSessionTracker());
}
}, []);
return (
<div>
<Script
src="/sdk/game-session-tracker.js"
strategy="afterInteractive"
onLoad="() => {
setTracker(new window.GameSessionTracker());
}"
/>
<!-- Your game UI -->
</div>
);
}Method B: Using Custom Document
// In pages/_document.js
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
<script src="/sdk/game-session-tracker.js"></script>
</body>
</Html>
);
}Then use the tracker in your game component:
import { useEffect, useState } from 'react';
export default function GameComponent() {
const [tracker, setTracker] = useState(null);
useEffect(() => {
// Wait for the SDK to load
const initTracker = () => {
if (window.GameSessionTracker) {
// The SDK will automatically detect the subdomain
setTracker(new window.GameSessionTracker());
} else {
// If not loaded yet, try again in a moment
setTimeout(initTracker, 100);
}
};
initTracker();
}, []);
// Function to call when game ends
const handleGameOver = (score) => {
if (tracker) {
tracker.endSession(score)
.then(response => {
console.log("Session recorded successfully", response);
})
.catch(error => {
console.error("Failed to record session", error);
});
}
};
// Function to display leaderboard
const showLeaderboard = () => {
if (tracker) {
tracker.getLeaderboard(10)
.then(response => {
const leaderboard = response.data.leaderboard;
// Display leaderboard in your UI
})
.catch(error => {
console.error("Failed to fetch leaderboard", error);
});
}
};
return (
<div>
<!-- Your game UI -->
<button onClick="() => handleGameOver(1500)">End Game</button>
<button onClick="showLeaderboard">Show Leaderboard</button>
</div>
);
}Option 2: Custom Implementation
If you prefer to implement session tracking yourself, follow these steps:
1. Create a session tracking service:
// services/sessionTracker.js
export default class SessionTracker {
constructor() {
this.token = null;
this.appVersion = null;
this.language = null;
this.sessionStartTime = null;
this.initialized = false;
// Initialize on creation
this.initialize();
}
initialize() {
if (typeof window === 'undefined') return; // Skip on server-side
const urlParams = new URLSearchParams(window.location.search);
this.token = urlParams.get('token') || this.generateRandomToken();
this.appVersion = urlParams.get('appVersion') || '1.0.0';
this.language = urlParams.get('language') || 'en';
this.sessionStartTime = new Date();
this.initialized = true;
}
generateRandomToken() {
return 'player-' + Math.random().toString(36).substring(2, 15);
}
async endSession(score) {
if (!this.initialized) {
console.error('Session tracker not initialized');
return;
}
const sessionEndTime = new Date();
const payload = {
token: this.token,
session_start_time: this.sessionStartTime.toISOString(),
session_end_time: sessionEndTime.toISOString(),
score: score,
app_version: this.appVersion,
language: this.language
};
// Get the subdomain from the current hostname
const hostname = window.location.hostname;
const parts = hostname.split('.');
const subdomain = parts.length > 2 ? parts[0] : '';
// REQUIRED: This exact endpoint must be called when a game session ends
try {
const response = await fetch(`/api/v1/games/${subdomain}/session-ended`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
return await response.json();
} catch (error) {
console.error('Failed to record session', error);
throw error;
}
}
async getLeaderboard(limit = 10) {
// Get the subdomain from the current hostname
const hostname = window.location.hostname;
const parts = hostname.split('.');
const subdomain = parts.length > 2 ? parts[0] : '';
try {
const response = await fetch(`/api/v1/games/${subdomain}/leaderboard?limit=${limit}`);
return await response.json();
} catch (error) {
console.error('Failed to fetch leaderboard', error);
throw error;
}
}
}2. Use the service in your game component:
// components/Game.js
import { useEffect, useState } from 'react';
import SessionTracker from '../services/sessionTracker';
export default function Game() {
const [tracker, setTracker] = useState(null);
useEffect(() => {
// Initialize the tracker on client-side only
if (typeof window !== 'undefined') {
// The tracker will automatically detect the subdomain
setTracker(new SessionTracker());
}
}, []);
const handleGameOver = async (score) => {
if (tracker) {
try {
const result = await tracker.endSession(score);
console.log('Session recorded successfully', result);
} catch (error) {
console.error('Error recording session', error);
}
}
};
return (
<div>
<!-- Your game UI -->
<button onClick="() => handleGameOver(1500)">End Game</button>
</div>
);
}Testing Your Implementation
Before uploading, test your session tracking implementation:
- Run your Next.js app locally with
yarn dev - Add URL parameters to your local development URL (e.g.,
http://localhost:3000/?token=test123&appVersion=1.0.0&language=en) - Use browser developer tools to verify network requests are being made correctly
In browser developer tools, check that the POST request to /api/v1/games/:subdomain/session-ended is being made with all required parameters.
Uploading to Jiran Games
Log in to Your Account
Sign in to your Jiran Games developer account.
Create a New Game
Click on "Add New Game" and fill in the required information:
- Game Name
- Description
- Game Logo (recommended size: 512x512px)
- Tags (to help users find your game)
- Subdomain (this will be your game's URL: yourgame.jiran.games)
Upload Your Game Bundle
Select the ZIP file containing your Next.js build and upload it.
Note: Our system will automatically detect your Next.js build structure (static or server) and serve it correctly.
Submit for Review
After uploading, your game will be marked as "pending_review". Our team will review it to ensure it meets our guidelines.
The review process typically takes 1-2 business days.