Guide to Creating a New Mini-App
Detailed guide to create a new mini-app for 54VIE Super App.
1. Create project structure
mkdir mini-programs/my-miniapp
cd mini-programs/my-miniapp
Directory structure
my-miniapp/
├── src/
│ ├── App.tsx # Entry point
│ ├── screens/
│ │ ├── HomeScreen.tsx
│ │ └── DetailScreen.tsx
│ ├── components/
│ ├── hooks/
│ └── services/
├── package.json
├── tsconfig.json
├── rspack.config.mjs # Module Federation config
└── manifest.json # Mini-app manifest
2. Package.json
{
"name": "my-miniapp",
"version": "1.0.0",
"main": "src/App.tsx",
"scripts": {
"start": "react-native start --port 9002",
"bundle:ios": "rspack build --config rspack.config.mjs --env platform=ios",
"bundle:android": "rspack build --config rspack.config.mjs --env platform=android"
},
"dependencies": {
"@54vie/kit": "workspace:*",
"@54vie/miniapp-sdk": "workspace:*",
"@react-navigation/native": "^7.0.0",
"@react-navigation/native-stack": "^7.0.0"
},
"devDependencies": {
"@54vie/core": "workspace:*",
"@callstack/repack": "^5.0.0",
"@rspack/core": "^1.0.0",
"typescript": "^5.0.0"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
}
3. Module Federation Config
// rspack.config.mjs
import { createRequire } from 'node:module';
import path from 'node:path';
import * as Repack from '@callstack/repack';
const require = createRequire(import.meta.url);
const STANDALONE = process.env.STANDALONE === 'true';
export default (env) => {
const { platform } = env;
return {
context: __dirname,
entry: './src/index.js',
output: {
path: path.join(__dirname, 'build', platform),
filename: 'index.bundle',
publicPath: Repack.getPublicPath({ platform }),
},
plugins: [
new Repack.RepackPlugin(),
new Repack.plugins.ModuleFederationPluginV2({
name: 'my_miniapp',
filename: 'my_miniapp.container.js',
exposes: {
'./App': './src/App.tsx',
},
shared: {
react: { singleton: true, eager: STANDALONE },
'react-native': { singleton: true, eager: STANDALONE },
'@react-navigation/native': { singleton: true },
'@react-navigation/native-stack': { singleton: true },
'@54vie/kit': { singleton: true },
'@54vie/miniapp-sdk': { singleton: true },
},
}),
],
};
};
4. Manifest.json
{
"appId": "com.54vie.my-miniapp",
"name": "My Mini App",
"version": "1.0.0",
"description": "Description of my mini app",
"icon": "https://cdn.54vie.vn/icons/my-miniapp.png",
"permissions": [
"storage",
"analytics"
],
"minHostVersion": "1.0.0"
}
5. App Entry Point
// src/App.tsx
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { ToastProvider } from '@54vie/kit';
import HomeScreen from './screens/HomeScreen';
import DetailScreen from './screens/DetailScreen';
const Stack = createNativeStackNavigator();
export default function App() {
return (
<ToastProvider>
<NavigationContainer independent>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
</ToastProvider>
);
}
NavigationContainer
Always use the independent prop for NavigationContainer in mini-apps to avoid conflicts with host navigation.
6. Using the SDK
// src/screens/HomeScreen.tsx
import React, { useEffect } from 'react';
import { View, FlatList } from 'react-native';
import { Button, Card, Text, Loading } from '@54vie/kit';
import {
useHostAuth,
useHostDevice,
storage,
analytics,
api,
} from '@54vie/miniapp-sdk';
export default function HomeScreen({ navigation }) {
const { user, isAuthenticated, requestLogin } = useHostAuth();
const { deviceInfo } = useHostDevice();
// Track screen view
useEffect(() => {
analytics.screen('HomeScreen');
}, []);
// Fetch data from API
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const data = await api.get('/my-miniapp/items');
setItems(data);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
// Handle not authenticated
if (!isAuthenticated) {
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
<Text>Please login to continue</Text>
<Button onPress={requestLogin}>Login</Button>
</View>
);
}
if (loading) return <Loading />;
return (
<View style={{ flex: 1 }}>
<Text>Welcome, {user.name}!</Text>
<FlatList
data={items}
renderItem={({ item }) => (
<Card onPress={() => navigation.navigate('Detail', { id: item.id })}>
<Text>{item.title}</Text>
</Card>
)}
/>
</View>
);
}
7. Storage Usage
import { storage } from '@54vie/miniapp-sdk';
// Save user preferences
await storage.set('preferences', {
theme: 'dark',
notifications: true,
});
// Load preferences
const prefs = await storage.get('preferences');
8. Build & Deploy
Development
# Start dev server
pnpm start
Production Build
# Build for iOS
pnpm bundle:ios
# Build for Android
pnpm bundle:android
Upload to CDN
# Zip the build folder
cd build/ios && zip -r ../../my-miniapp-ios.zip . && cd ../..
cd build/android && zip -r ../../my-miniapp-android.zip . && cd ../..
# Upload to R2/S3
aws s3 cp my-miniapp-ios.zip s3://54vie-cdn/mini-apps/my-miniapp/1.0.0/ios.zip
aws s3 cp my-miniapp-android.zip s3://54vie-cdn/mini-apps/my-miniapp/1.0.0/android.zip
9. Register in Backend
Call the API to register the mini-app:
curl -X POST https://api.54vie.vn/v1/admin/mini-programs \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"appId": "com.54vie.my-miniapp",
"name": "My Mini App",
"version": "1.0.0",
"manifestUrl": "https://cdn.54vie.vn/mini-apps/my-miniapp/1.0.0/mf-manifest.json",
"bundleZipUrl": "https://cdn.54vie.vn/mini-apps/my-miniapp/1.0.0/${platform}.zip",
"permissions": ["storage", "analytics"],
"minHostVersion": "1.0.0"
}'
Checklist
- Create project structure
- Configure package.json
- Configure rspack.config.mjs
- Create manifest.json
- Implement App.tsx with NavigationContainer
- Use @54vie/kit for UI
- Use @54vie/miniapp-sdk for host services
- Track analytics events
- Test on iOS and Android
- Build and upload bundles
- Register in backend