Minastix Logo
Self-Hosting Technical Specifications

Overview

This document provides complete specifications for hosting the Minastix React frontend on your own server while maintaining Base44 backend integration for authentication, database, and functions.

⚠️ Important: This is an unsupported configuration

Base44 apps are designed to be hosted on Base44. Self-hosting requires manual configuration and maintenance.

1. Project Setup

Initialize React Project

npm create vite@latest minastix-frontend -- --template react
cd minastix-frontend
npm install

Install Required Dependencies

npm install @base44/sdk@^0.8.3 \
  react-router-dom@^7.2.0 \
  @tanstack/react-query@^5.84.1 \
  @radix-ui/react-accordion@^1.2.3 \
  @radix-ui/react-alert-dialog@^1.1.6 \
  @radix-ui/react-avatar@^1.1.3 \
  @radix-ui/react-checkbox@^1.1.4 \
  @radix-ui/react-dialog@^1.1.6 \
  @radix-ui/react-dropdown-menu@^2.1.6 \
  @radix-ui/react-label@^2.1.2 \
  @radix-ui/react-popover@^1.1.6 \
  @radix-ui/react-select@^2.1.6 \
  @radix-ui/react-tabs@^1.1.3 \
  @radix-ui/react-toast@^1.2.2 \
  @radix-ui/react-slot@^1.1.2 \
  lucide-react@^0.475.0 \
  framer-motion@^11.16.4 \
  date-fns@^3.6.0 \
  lodash@^4.17.21 \
  moment@^2.30.1 \
  recharts@^2.15.4 \
  react-markdown@^9.0.1 \
  sonner@^2.0.1 \
  class-variance-authority@^0.7.1 \
  clsx@^2.1.1 \
  tailwind-merge@^3.0.2

Install Tailwind CSS

npm install -D tailwindcss@latest postcss autoprefixer
npx tailwindcss init -p

2. Project Structure

minastix-frontend/
├── src/
│   ├── api/
│   │   └── base44Client.js          # Base44 SDK initialization
│   ├── components/
│   │   ├── ui/                      # shadcn/ui components
│   │   ├── home/
│   │   │   ├── HeroSection.jsx
│   │   │   └── ProgramCard.jsx
│   │   ├── enrollment/
│   │   └── admin/
│   ├── pages/
│   │   ├── Home.js
│   │   ├── Enroll.js
│   │   ├── Shop.js
│   │   ├── Cart.js
│   │   ├── Dashboard.js
│   │   ├── EnrollmentSuccess.js
│   │   ├── CheckoutSuccess.js
│   │   └── NotFound.js
│   ├── utils/
│   │   └── index.js                 # Utility functions
│   ├── lib/
│   │   └── utils.js                 # cn() helper for Tailwind
│   ├── Layout.js                    # App layout wrapper
│   ├── App.jsx                      # Main app component
│   ├── main.jsx                     # Entry point
│   └── index.css                    # Global styles
├── public/
│   └── wp-content/
│       └── uploads/
│           └── 2021/
│               └── 10/
│                   └── MONKEYNASTIX-THEME.mp3
├── package.json
├── vite.config.js
└── tailwind.config.js

3. Configuration Files

vite.config.js

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'https://base44.app',
        changeOrigin: true,
        secure: false,
      }
    }
  }
})

tailwind.config.js

module.exports = {
  darkMode: ["class"],
  content: [
    './pages/**/*.{js,jsx}',
    './components/**/*.{js,jsx}',
    './app/**/*.{js,jsx}',
    './src/**/*.{js,jsx}',
  ],
  theme: {
    container: {
      center: true,
      padding: "2rem",
      screens: {
        "2xl": "1400px",
      },
    },
    extend: {
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        secondary: {
          DEFAULT: "hsl(var(--secondary))",
          foreground: "hsl(var(--secondary-foreground))",
        },
        destructive: {
          DEFAULT: "hsl(var(--destructive))",
          foreground: "hsl(var(--destructive-foreground))",
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))",
        },
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))",
        },
        popover: {
          DEFAULT: "hsl(var(--popover))",
          foreground: "hsl(var(--popover-foreground))",
        },
        card: {
          DEFAULT: "hsl(var(--card))",
          foreground: "hsl(var(--card-foreground))",
        },
      },
      borderRadius: {
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
        sm: "calc(var(--radius) - 4px)",
      },
      keyframes: {
        "accordion-down": {
          from: { height: 0 },
          to: { height: "var(--radix-accordion-content-height)" },
        },
        "accordion-up": {
          from: { height: "var(--radix-accordion-content-height)" },
          to: { height: 0 },
        },
      },
      animation: {
        "accordion-down": "accordion-down 0.2s ease-out",
        "accordion-up": "accordion-up 0.2s ease-out",
      },
    },
  },
  plugins: [require("tailwindcss-animate")],
}

package.json (scripts section)

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}

4. Base44 SDK Configuration

src/api/base44Client.js

import { createClient } from '@base44/sdk';

export const base44 = createClient({
  appId: '69010ca5cc964c7fd44065a4',
  apiUrl: 'https://base44.app/api',
  
  // Optional: Use local storage for auth token
  storage: {
    getItem: (key) => localStorage.getItem(key),
    setItem: (key, value) => localStorage.setItem(key, value),
    removeItem: (key) => localStorage.removeItem(key),
  }
});

src/utils/index.js

export function createPageUrl(pageName, params = {}) {
  const searchParams = new URLSearchParams(params);
  const queryString = searchParams.toString();
  return `/${pageName}${queryString ? '?' + queryString : ''}`;
}

src/lib/utils.js

import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs) {
  return twMerge(clsx(inputs))
}

5. Router Setup

src/App.jsx

import React from 'react';
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import Layout from './Layout';

// Import all pages
import HomePage from './pages/Home';
import EnrollPage from './pages/Enroll';
import ShopPage from './pages/Shop';
import CartPage from './pages/Cart';
import DashboardPage from './pages/Dashboard';
import EnrollmentSuccessPage from './pages/EnrollmentSuccess';
import CheckoutSuccessPage from './pages/CheckoutSuccess';
import NotFoundPage from './pages/NotFound';

const queryClient = new QueryClient();

function AppContent() {
  const location = useLocation();
  const currentPageName = location.pathname.replace('/', '') || 'Home';
  
  return (
    <Layout currentPageName={currentPageName}>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/Home" element={<HomePage />} />
        <Route path="/Enroll" element={<EnrollPage />} />
        <Route path="/Shop" element={<ShopPage />} />
        <Route path="/Cart" element={<CartPage />} />
        <Route path="/Dashboard" element={<DashboardPage />} />
        <Route path="/EnrollmentSuccess" element={<EnrollmentSuccessPage />} />
        <Route path="/CheckoutSuccess" element={<CheckoutSuccessPage />} />
        <Route path="*" element={<NotFoundPage />} />
      </Routes>
    </Layout>
  );
}

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>
        <AppContent />
      </BrowserRouter>
    </QueryClientProvider>
  );
}

src/main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

6. Styling Setup

src/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --card: 0 0% 100%;
    --card-foreground: 222.2 84% 4.9%;
    --popover: 0 0% 100%;
    --popover-foreground: 222.2 84% 4.9%;
    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;
    --secondary: 210 40% 96.1%;
    --secondary-foreground: 222.2 47.4% 11.2%;
    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;
    --destructive: 0 84.2% 60.2%;
    --destructive-foreground: 210 40% 98%;
    --border: 214.3 31.8% 91.4%;
    --input: 214.3 31.8% 91.4%;
    --ring: 222.2 84% 4.9%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --card: 222.2 84% 4.9%;
    --card-foreground: 210 40% 98%;
    --popover: 222.2 84% 4.9%;
    --popover-foreground: 210 40% 98%;
    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 11.2%;
    --secondary: 217.2 32.6% 17.5%;
    --secondary-foreground: 210 40% 98%;
    --muted: 217.2 32.6% 17.5%;
    --muted-foreground: 215 20.2% 65.1%;
    --accent: 217.2 32.6% 17.5%;
    --accent-foreground: 210 40% 98%;
    --destructive: 0 62.8% 30.6%;
    --destructive-foreground: 210 40% 98%;
    --border: 217.2 32.6% 17.5%;
    --input: 217.2 32.6% 17.5%;
    --ring: 212.7 26.8% 83.9%;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground;
  }
}

7. Building for Production

Build Command

npm run build

This creates a dist/ folder with optimized production files.

Server Configuration

For proper routing, configure your server to redirect all requests to index.html:

Nginx

server {
  listen 80;
  server_name minastix.com;
  root /var/www/minastix/dist;
  index index.html;

  # Serve the MP3 file directly
  location /wp-content/uploads/2021/10/MONKEYNASTIX-THEME.mp3 {
    alias /var/www/minastix/dist/wp-content/uploads/2021/10/MONKEYNASTIX-THEME.mp3;
    add_header Content-Type audio/mpeg;
  }

  # Handle React routing
  location / {
    try_files $uri $uri/ /index.html;
  }

  # Cache static assets
  location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
  }
}

Apache (.htaccess)

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  
  # Serve MP3 file directly
  RewriteRule ^wp-content/uploads/2021/10/MONKEYNASTIX-THEME\.mp3$ /wp-content/uploads/2021/10/MONKEYNASTIX-THEME.mp3 [L]
  
  # Handle React routing
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

8. Environment Variables

Create a .env file:

VITE_BASE44_APP_ID=69010ca5cc964c7fd44065a4
VITE_BASE44_API_URL=https://base44.app/api

Update base44Client.js to use these:

export const base44 = createClient({
  appId: import.meta.env.VITE_BASE44_APP_ID,
  apiUrl: import.meta.env.VITE_BASE44_API_URL,
});

9. Deployment Checklist

Copy all page files from Base44 to src/pages/
Copy all component files to src/components/
Copy Layout.js to src/
Install all shadcn/ui components used in your app
Place MONKEYNASTIX-THEME.mp3 in public/wp-content/uploads/2021/10/
Test all Base44 API calls work correctly
Configure CORS on Base44 if needed
Set up SSL certificate (Let's Encrypt recommended)
Configure server for React routing
Test enrollment, payment, and cart flows

10. Important Notes

🚨 CORS Configuration Required

You may need to contact Base44 support to whitelist your domain for CORS requests to their API.

ℹ️ Backend Functions

All backend functions (fetchSchools, sendWelcomeEmails, etc.) will continue running on Base44 infrastructure.

⚠️ Authentication

Authentication is handled by Base44. Login/logout flows will redirect to Base44's auth pages.

✅ WordPress Path Solution

Place the MP3 file in your public folder at the exact WordPress path, and configure your server to serve it directly.

11. Testing Locally

# Start development server
npm run dev

# Access at http://localhost:3000
# Test the MP3 path:
# http://localhost:3000/wp-content/uploads/2021/10/MONKEYNASTIX-THEME.mp3

12. Support & Troubleshooting

Issue: 404 on refresh

→ Configure server to redirect all requests to index.html

Issue: CORS errors

→ Contact Base44 support to whitelist your domain

Issue: Auth not working

→ Ensure Base44 SDK is configured with correct appId and apiUrl

Issue: MP3 file not found

→ Verify file is in public/wp-content/uploads/2021/10/ and server is configured correctly

© 2026 Minastix. All Rights Reserved.

Empowering children through movement and play.

Website designed and powered by Webtron