Skip to main content

πŸš€ Setting Up Nodeblocks with VITE from Scratch

Build modern React 19 applications with Nodeblocks components using VITE for lightning-fast development ⚑

This comprehensive guide will walk you through creating a complete application from scratch using VITE, React 19, and Nodeblocks frontend components.


πŸ“‹ Prerequisites​


🎯 Project Setup​

Step 1: Create VITE Project​

# Create new VITE project with React and TypeScript
npm create vite@latest my-nodeblocks-app -- --template react-ts

# Navigate to project directory
cd my-nodeblocks-app

# Install react dependencies
npm install react@^19.0.0 react-dom@^19.0.0
npm install --save-dev @types/react@^19.0.0 @types/react-dom@^19.0.0

# Install other dependencies
npm install

# Verify React 19 is installed
npm list react react-dom

Step 2: Configure NPM Registry Access​

Before installing Nodeblocks packages, you need to configure access to the private npm registry. Create a .npmrc file in your project root:

# Create .npmrc file in project root
touch .npmrc

Add the following configuration to your .npmrc file:

@nodeblocks:registry=https://registry.npmjs.org
@basaldev:registry=https://registry.npmjs.org
//registry.npmjs.org/:_authToken=${NODEBLOCKS_DEV_TOKEN}

Important: You'll need to set the NODEBLOCKS_DEV_TOKEN environment variable with your access token. Contact the Nodeblocks team to obtain your development token.

# Set your Nodeblocks development token
export NODEBLOCKS_DEV_TOKEN=your_token_here

Step 3: Install Nodeblocks Dependencies​

Install the essential Nodeblocks packages:

# Core domain package for state management
npm install @nodeblocks/matching-app-domain

# Theme package (recommended)
npm install @nodeblocks/frontend-theme

# Essential UI blocks
npm install @nodeblocks/frontend-navbar-block@0.2.0
npm install @nodeblocks/frontend-footer-block@0.2.0
npm install @nodeblocks/frontend-hero-block@0.2.0
npm install @nodeblocks/frontend-signin-block@0.3.0
npm install @nodeblocks/frontend-signup-block@0.4.0

# Product management blocks
npm install @nodeblocks/frontend-create-product-block@0.5.0
npm install @nodeblocks/frontend-list-products-grid-block@0.2.0

# Organization management blocks
npm install @nodeblocks/frontend-create-organization-block

# User management blocks
npm install @nodeblocks/frontend-basic-information-block
npm install @nodeblocks/frontend-list-users-block@0.2.0

πŸ—οΈ Basic Application Structure​

Step 4: Create Main Application Component​

Replace the contents of src/App.tsx:

import React from 'react'
import { ThemeProvider } from '@nodeblocks/frontend-theme'
import { Navbar } from '@nodeblocks/frontend-navbar-block'
import { Footer } from '@nodeblocks/frontend-footer-block'
import { MatchingAppDomain } from '@nodeblocks/matching-app-domain'
import { HomePage } from './pages/HomePage'
import { LoginPage } from './pages/LoginPage'
import { SignUpPage } from './pages/SignUpPage'
import { ProductsPage } from './pages/ProductsPage'
import './App.css'

function App() {
return (
<ThemeProvider>
<MatchingAppDomain
applicationType={['demand']}
serviceEndpoints={{
user: import.meta.env.VITE_USER_SERVICE_URL || 'https://user-service.example.com',
auth: import.meta.env.VITE_AUTH_SERVICE_URL || 'https://auth-service.example.com',
organization: import.meta.env.VITE_ORG_SERVICE_URL || 'https://org-service.example.com',
catalog: import.meta.env.VITE_CATALOG_SERVICE_URL || 'https://catalog-service.example.com',
chat: import.meta.env.VITE_CHAT_SERVICE_URL || 'https://chat-service.example.com',
order: import.meta.env.VITE_ORDER_SERVICE_URL || 'https://order-service.example.com',
}}
logoutRedirectUrl={'/login'}
loggedInRedirectUrl={'/products'}
sessionType="local_storage">
{({state, middleware, updateState}) => {
return (
<div className="app">
{/* Navigation Bar */}
<Navbar
leftContent={
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<Navbar.Logo src="/vite.svg" alt="Company Logo" style={{ height: 32 }} />
<span className="brand-name">MyApp</span>
</div>
}
centerContent={
<div style={{ display: 'flex', gap: 16 }}>
<Navbar.Link href="/">Home</Navbar.Link>
<Navbar.Link href="/positions">Products</Navbar.Link>
</div>
}
rightContent={
state.loggedInUser ? (
<Navbar.ButtonLink onClick={() => middleware.onActionLogout()}>
Logout
</Navbar.ButtonLink>
) : (
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<Navbar.Link href="/login">Login</Navbar.Link>
<Navbar.ButtonLink href="/sign-up">Sign Up</Navbar.ButtonLink>
</div>
)
}
sx={{ backgroundColor: 'white', boxShadow: 1 }}>
<Navbar.Left />
<Navbar.Center />
<Navbar.Right />
</Navbar>

{/* Main Content */}
<main className="main-content">
{renderPage(state.currentPage, state, middleware, updateState)}
</main>

{/* Footer */}
<Footer
logoSrc="/vite.svg"
content={
<div className="footer-content">
<div className="footer-section">
<h4>Company</h4>
<ul>
<li><a href="/about">About Us</a></li>
<li><a href="/careers">Careers</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<div className="footer-section">
<h4>Support</h4>
<ul>
<li><a href="/help">Help Center</a></li>
<li><a href="/docs">Documentation</a></li>
<li><a href="/api">API</a></li>
</ul>
</div>
</div>
}
copyright="Β© 2025 Your Company Name. All rights reserved.">
<Footer.Logo />
<Footer.Content />
<Footer.Copyright />
</Footer>
</div>
)
}}
</MatchingAppDomain>
</ThemeProvider>
)
}

// Page routing function
function renderPage(currentPage: string, state: any, middleware: any, updateState: any) {
switch (currentPage) {
case 'home':
return <HomePage />
case 'login':
return <LoginPage middleware={middleware} />
case 'sign_up':
return <SignUpPage middleware={middleware} />
case 'positions_list':
return <ProductsPage />
default:
return <div>Not Found</div>
}
}

export default App

πŸ’‘ Note: The ThemeProvider wraps the entire app to provide consistent MUI theming. See the Theming Guide for customization options.


πŸ“± Creating Page Components​

Step 5: Home Page with Hero Block​

Create src/pages/HomePage.tsx:

import React from 'react'
import { Hero } from '@nodeblocks/frontend-hero-block'

export function HomePage() {
return (
<div className="home-page">
<Hero
byline="Welcome to Nodeblocks"
secondaryText="Build modern applications faster with our comprehensive component library"
imageUrl="https://images.unsplash.com/photo-1557804506-669a67965ba0?auto=format&fit=crop&w=1920&q=80"
buttonText="Get Started"
onClickButton={() => console.log("Get Started")}>
<Hero.HeroContent>
<Hero.HeroContent.HeroByline />
<Hero.HeroContent.SecondaryText />
<Hero.HeroContent.ActionButton />
</Hero.HeroContent>
<Hero.HeroImg />
</Hero>

<section className="features-section">
<div className="container">
<h2>Why Choose Nodeblocks?</h2>
<div className="features-grid">
<div className="feature-card">
<h3>πŸš€ Fast Development</h3>
<p>Reduce development time by 70% with pre-built components</p>
</div>
<div className="feature-card">
<h3>🎨 Customizable</h3>
<p>Fully customizable components that adapt to your brand</p>
</div>
<div className="feature-card">
<h3>⚑ Performance</h3>
<p>Optimized for speed and built with modern React practices</p>
</div>
</div>
</div>
</section>
</div>
)
}

πŸ“š Learn more: See Hero Block Documentation for advanced customization options.

Step 6: Authentication Pages​

Create src/pages/LoginPage.tsx:

import React from 'react'
import { SignIn } from '@nodeblocks/frontend-signin-block'

interface LoginPageProps {
middleware: {
onActionLogin: (email: string, password: string) => Promise<void>
}
}

export function LoginPage({ middleware }: LoginPageProps) {
return (
<div className="auth-page">
<div className="auth-container">
<SignIn
onChange={(setError, getValues, clearErrors) => {
const values = getValues()

// Custom validation
if (!values.email) {
setError('email', { message: 'Email is required', type: 'required' })
} else if (!/\S+@\S+\.\S+/.test(values.email)) {
setError('email', { message: 'Email is invalid', type: 'pattern' })
} else {
clearErrors('email')
}
}}
onSubmit={async (formData) => {
try {
await middleware.onActionLogin(formData.email || '', formData.password || '')
console.log('Login successful')
} catch (error) {
console.error('Login failed:', error)
}
}}
signupUrl="/sign-up"
resetPasswordUrl="/reset-password"
sx={{ maxWidth: 400, mx: 'auto' }}>
<SignIn.SignInTitle>Welcome Back</SignIn.SignInTitle>
<SignIn.EmailField label="Email Address" placeholder="Enter your email" />
<SignIn.PasswordField label="Password" placeholder="Enter your password" />
<SignIn.SignInButton>Sign In</SignIn.SignInButton>
<SignIn.GotoSignUp>
<span>Don't have an account? <a href="/sign-up">Sign up</a></span>
</SignIn.GotoSignUp>
<SignIn.ResetPassword>
<a href="/reset-password">Forgot your password?</a>
</SignIn.ResetPassword>
</SignIn>
</div>
</div>
)
}

Create src/pages/SignUpPage.tsx:

import React from 'react'
import { SignUp } from '@nodeblocks/frontend-signup-block'

interface SignUpPageProps {
middleware: {
onActionRegister: (formData: {
email?: string;
password?: string;
agreesPrivacyPolicy?: boolean;
agreesUserAgreement?: boolean;
}) => Promise<void>
}
}

export function SignUpPage({ middleware }: SignUpPageProps) {
return (
<div className="auth-page">
<div className="auth-container">
<SignUp
onChange={(setError, getValues, clearErrors) => {
const { email, password } = getValues()

// Custom validation
if (email && !email.includes('@')) {
setError('email', { message: 'Please enter a valid email' })
} else {
clearErrors('email')
}

if (password && password.length < 8) {
setError('password', { message: 'Password must be at least 8 characters' })
} else {
clearErrors('password')
}
}}
onSubmit={async (formData) => {
try {
await middleware.onActionRegister(formData)
console.log('Registration successful')
} catch (error) {
console.error('Registration failed:', error)
}
}}
termsOfUseUrl="/terms"
privacyPolicyUrl="/privacy"
loginUrl="/login"
sx={{ maxWidth: 400, mx: 'auto' }}>
<SignUp.SignUpTitle>Create Your Account</SignUp.SignUpTitle>
<SignUp.EmailField label="Email Address" placeholder="Enter your email" />
<SignUp.PasswordField label="Password" placeholder="Create a secure password" />
<SignUp.TermsOfUse name="agreesUserAgreement" label="I agree to the terms of use" />
<SignUp.PrivacyPolicy name="agreesPrivacyPolicy" label="I agree to the privacy policy" />
<SignUp.SignUpButton>Create Account</SignUp.SignUpButton>
<SignUp.GotoSignIn>
<span>Already have an account? <a href="/login">Sign in</a></span>
</SignUp.GotoSignIn>
</SignUp>
</div>
</div>
)
}

πŸ“š Learn more: See Sign In Block and Sign Up Block documentation for advanced customization.

Step 7: Products Page​

Create src/pages/ProductsPage.tsx:

import React, { useState, ComponentProps } from 'react'
import { CreateProduct } from '@nodeblocks/frontend-create-product-block'
import { ListProductsGrid } from '@nodeblocks/frontend-list-products-grid-block'

export function ProductsPage() {
const [showCreateForm, setShowCreateForm] = useState(false)
const [products, setProducts] = useState<ComponentProps<typeof ListProductsGrid.Items.GridCard>[]>([
{
title: 'Modern Laptop',
subtitle: 'High-performance laptop for professionals',
imageUrl: 'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=300',
chips: [{ label: 'New' }],
tags: [{ icon: 'shopping_bag' as const, label: 'Electronics' }],
linkProps: { href: '/products/1', onNavigate: () => console.log('View product 1') }
},
{
title: 'Wireless Headphones',
subtitle: 'Premium wireless headphones with noise cancellation',
imageUrl: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=300',
chips: [{ label: 'Sale' }],
tags: [{ icon: 'shopping_bag' as const, label: 'Electronics' }],
linkProps: { href: '/products/2', onNavigate: () => console.log('View product 2') }
}
])

const categoryOptions = [
{ value: 'electronics', label: 'Electronics' },
{ value: 'clothing', label: 'Clothing' },
{ value: 'home', label: 'Home & Garden' },
{ value: 'books', label: 'Books' },
{ value: 'sports', label: 'Sports & Recreation' }
]

return (
<div className="products-page">
<div className="container">
<div className="page-header">
<h1>Product Management</h1>
<button
className="btn-primary"
onClick={() => setShowCreateForm(!showCreateForm)}>
{showCreateForm ? 'View Products' : 'Add Product'}
</button>
</div>

{showCreateForm ? (
<div className="create-product-section">
<CreateProduct
categoryOptions={categoryOptions}
onSubmit={(formData) => {
console.log('Creating product:', formData)
const newProduct = {
title: formData.name || '',
subtitle: formData.description || '',
imageUrl: 'https://via.placeholder.com/300',
chips: [{ label: 'New' }],
tags: [{ icon: 'shopping_bag' as const, label: String(formData.category) || 'General' }],
linkProps: { href: `/products/${Date.now()}`, onNavigate: () => {} }
}
setProducts([...products, newProduct])
setShowCreateForm(false)
}}
onChange={(setError, getValues, clearErrors) => {
const values = getValues()
// Add validation logic here if needed
}}
onAcceptAttachment={(files) => console.log('Files accepted:', files)}
onClearAttachment={() => console.log('Attachment cleared')}>
<CreateProduct.MainInfo>
<CreateProduct.MainInfo.SectionTitle>Product Information</CreateProduct.MainInfo.SectionTitle>
<CreateProduct.MainInfo.Dropzone />
<CreateProduct.MainInfo.NameField />
</CreateProduct.MainInfo>

<CreateProduct.BasicInfo>
<CreateProduct.BasicInfo.Title />
<CreateProduct.BasicInfo.CategoryField />
<CreateProduct.BasicInfo.DescriptionField />
</CreateProduct.BasicInfo>

<CreateProduct.Actions>
<CreateProduct.Actions.SubmitButton>Create Product</CreateProduct.Actions.SubmitButton>
</CreateProduct.Actions>
</CreateProduct>
</div>
) : (
<div className="products-list-section">
<ListProductsGrid listProductsGridTitle="Featured Products" subtitle="Latest Collection">
<ListProductsGrid.Title />
<ListProductsGrid.Items>
{products.map((product, index) => (
<ListProductsGrid.Items.GridCard key={index} {...product} />
))}
</ListProductsGrid.Items>
</ListProductsGrid>
</div>
)}
</div>
</div>
)
}

πŸ“š Learn more: See Create Product Block and List Products Grid Block for advanced customization.


🎨 Styling Your Application​

Step 8: Add Custom Styles​

Update src/App.css:

/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
line-height: 1.6;
color: #333;
background-color: #f8fafc;
}

.app {
min-height: 100vh;
display: flex;
flex-direction: column;
}

.main-content {
flex: 1;
}

/* Container */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}

/* Navigation styles */
.navbar-logo {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: bold;
color: #1a202c;
}

.brand-name {
font-size: 1.5rem;
}

.main-navigation {
display: flex;
gap: 2rem;
}

.nav-link {
text-decoration: none;
color: #4a5568;
font-weight: 500;
transition: color 0.2s;
}

.nav-link:hover {
color: #2d3748;
}

.auth-buttons {
display: flex;
gap: 1rem;
align-items: center;
}

.btn-login, .btn-signup, .btn-logout {
padding: 0.5rem 1rem;
border-radius: 6px;
text-decoration: none;
font-weight: 500;
transition: all 0.2s;
border: none;
cursor: pointer;
}

.btn-login {
color: #4a5568;
background: transparent;
}

.btn-login:hover {
background-color: #f7fafc;
}

.btn-signup, .btn-logout {
background-color: #3182ce;
color: white;
}

.btn-signup:hover, .btn-logout:hover {
background-color: #fff;
color: #3182ce;
}

/* Page styles */
.home-page {
min-height: calc(100vh - 160px);
}

.features-section {
padding: 4rem 0;
background: white;
}

.features-section h2 {
text-align: center;
margin-bottom: 3rem;
font-size: 2.5rem;
color: #2d3748;
}

.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 2rem;
}

.feature-card {
padding: 2rem;
border-radius: 12px;
background: #f7fafc;
border: 1px solid #e2e8f0;
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
}

.feature-card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}

.feature-card h3 {
margin-bottom: 1rem;
color: #2d3748;
}

/* Auth pages */
.auth-page {
min-height: calc(100vh - 160px);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}

.auth-container {
width: 100%;
max-width: 500px;
}

/* Products page */
.products-page {
padding: 2rem 0;
}

.page-header {
display: flex;
justify-content: between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid #e2e8f0;
}

.page-header h1 {
color: #2d3748;
font-size: 2.5rem;
}

.btn-primary {
background-color: #3182ce;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}

.btn-primary:hover {
background-color: #2c5282;
}


input {
background-color: #fff;
}


.create-product-section, .products-list-section {
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

/* Footer styles */
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}

.footer-section h4 {
margin-bottom: 1rem;
color: white;
}

.footer-section ul {
list-style: none;
}

.footer-section li {
margin-bottom: 0.5rem;
}

.footer-section a {
color: #4a5568;
text-decoration: none;
transition: color 0.2s;
}

.footer-section a:hover {
color: #2d3748;
}

/* Responsive design */
@media (max-width: 768px) {
.main-navigation {
display: none;
}

.features-grid {
grid-template-columns: 1fr;
}

.page-header {
flex-direction: column;
gap: 1rem;
align-items: stretch;
}

.auth-buttons {
flex-direction: column;
}
}

πŸ”§ Environment Configuration​

Step 9: Environment Variables​

Create .env file in your project root:

# API Endpoints
VITE_USER_SERVICE_URL=https://user-service.example.com
VITE_AUTH_SERVICE_URL=https://auth-service.example.com
VITE_ORG_SERVICE_URL=https://organization-service.example.com
VITE_CATALOG_SERVICE_URL=https://catalog-service.example.com
VITE_CHAT_SERVICE_URL=https://chat-service.example.com
VITE_ORDER_SERVICE_URL=https://order-service.example.com

# Application Configuration
VITE_APP_NAME=MyNodeBlocksApp
VITE_APP_VERSION=1.0.0

Create .env.development for development:

# Development API Endpoints
VITE_USER_SERVICE_URL=http://localhost:8001
VITE_AUTH_SERVICE_URL=http://localhost:8002
VITE_ORG_SERVICE_URL=http://localhost:8003
VITE_CATALOG_SERVICE_URL=http://localhost:8004
VITE_CHAT_SERVICE_URL=http://localhost:8005
VITE_ORDER_SERVICE_URL=http://localhost:8006

πŸš€ Running Your Application​

Step 10: Start Development Server​

# Start the development server
npm run dev

# Your application will be available at:
# http://localhost:3000

Step 11: Build for Production​

# Build for production
npm run build

# Preview production build
npm run preview

πŸ“ˆ Advanced Features​

Adding More Blocks​

As your application grows, you can add more specialized blocks:

# Organization management
npm install @nodeblocks/frontend-list-organizations-block

# Order management
npm install @nodeblocks/frontend-list-order-table-block

# Chat functionality
npm install @nodeblocks/frontend-chat-conversation-block@0.2.0
npm install @nodeblocks/frontend-chat-conversation-list-block

# Advanced UI components
npm install @nodeblocks/frontend-filter-search-panel-block@0.3.0
npm install @nodeblocks/frontend-breadcrumb-block
npm install @nodeblocks/frontend-side-navigation-block@0.3.0

Custom Block Overriding​

All Nodeblocks components support the powerful block overriding pattern:

<SignIn onSubmit={handleSubmit} onChange={handleChange}>
{({ defaultBlocks, defaultBlockOrder }) => ({
blocks: {
...defaultBlocks,
// Override specific blocks
signInTitle: {
...defaultBlocks.signInTitle,
props: {
...defaultBlocks.signInTitle.props,
children: 'Welcome to MyApp',
sx: { color: 'primary.main', fontWeight: 'bold' }
}
},
emailField: {
...defaultBlocks.emailField,
props: {
...defaultBlocks.emailField.props,
label: 'Corporate Email',
placeholder: 'name@company.com'
}
}
},
blockOrder: defaultBlockOrder
})}
</SignIn>

Learn More: For comprehensive examples and advanced block overriding techniques, see our Advanced Usage Guide.


πŸ”— Useful Resources​


Built with ❀️ using Nodeblocks and VITE for maximum development speed and performance