# Lockness Components
Reusable JSX components for server-side rendered views.
## Basic Component
```tsx
// src/view/components/button.tsx
export const Button = (props: { text: string; href: string }) => {
return (
{props.text}
)
}
```
**Usage:**
```tsx
import { Button } from '@view/components/button.tsx'
export const HomePage = () => {
return (
Welcome
)
}
```
## Props and Types
```tsx
type CardProps = {
title: string
description: string
imageUrl?: string
children?: any
}
export const Card = (props: CardProps) => {
return (
{props.imageUrl &&

}
{props.title}
{props.description}
{props.children}
)
}
```
**Usage with children:**
```tsx
```
## Layout Components
```tsx
// src/view/layouts/app_layout.tsx
export const AppLayout = (props: {
title: string
children: any
}) => {
return (
{props.title}
{props.children}
)
}
```
**Usage:**
```tsx
import { AppLayout } from '@view/layouts/app_layout.tsx'
export const AboutPage = () => {
return (
About Us
We are awesome!
)
}
```
## Conditional Rendering
```tsx
export const Alert = (props: {
message?: string
type?: 'success' | 'error' | 'info'
}) => {
if (!props.message) return null
const colors = {
success: 'bg-green-100 text-green-800 border-green-300',
error: 'bg-red-100 text-red-800 border-red-300',
info: 'bg-blue-100 text-blue-800 border-blue-300',
}
const colorClass = colors[props.type || 'info']
return (
{props.message}
)
}
```
## List Rendering
```tsx
type Post = {
id: number
title: string
content: string
}
export const PostList = (props: { posts: Post[] }) => {
return (
{props.posts.length === 0 ? (
No posts found
) : (
{props.posts.map(post => (
-
{post.title}
{post.content}
))}
)}
)
}
```
## Form Components
```tsx
type InputProps = {
label: string
name: string
type?: string
value?: string
error?: string
required?: boolean
}
export const Input = (props: InputProps) => {
return (
{props.error && (
{props.error}
)}
)
}
export const Form = (props: {
action: string
method?: string
children: any
}) => {
return (
)
}
```
**Usage:**
```tsx
```
## Dynamic Attributes
```tsx
export const Badge = (props: {
text: string
variant?: 'primary' | 'secondary' | 'danger'
}) => {
const variants = {
primary: 'bg-blue-500 text-white',
secondary: 'bg-gray-500 text-white',
danger: 'bg-red-500 text-white',
}
return (
{props.text}
)
}
```
## Composition
```tsx
// Card component
export const Card = (props: { children: any; className?: string }) => {
return (
{props.children}
)
}
// CardHeader component
export const CardHeader = (props: { children: any }) => {
return (
{props.children}
)
}
// CardBody component
export const CardBody = (props: { children: any }) => {
return {props.children}
}
// CardFooter component
export const CardFooter = (props: { children: any }) => {
return (
{props.children}
)
}
```
**Usage:**
```tsx
Title
Content here
```
## Icons as Components
```tsx
export const CheckIcon = () => (
)
export const UserIcon = () => (
)
```
## Helper Components
### Pagination
```tsx
type PaginationProps = {
currentPage: number
totalPages: number
baseUrl: string
}
export const Pagination = (props: PaginationProps) => {
const pages = Array.from({ length: props.totalPages }, (_, i) => i + 1)
return (
)
}
```
### Loading Spinner
```tsx
export const Spinner = () => {
return (
)
}
```
### Avatar
```tsx
export const Avatar = (props: {
src?: string
alt: string
size?: 'sm' | 'md' | 'lg'
}) => {
const sizes = {
sm: 'w-8 h-8',
md: 'w-12 h-12',
lg: 'w-16 h-16',
}
return props.src ? (
) : (
{props.alt.charAt(0).toUpperCase()}
)
}
```
## Best Practices
- **Pure components** - No side effects, only render
- **Type props** - Use TypeScript interfaces
- **Default props** - Provide sensible defaults
- **Composable** - Break into smaller components
- **Reusable** - Keep generic, avoid business logic
- **Server-side only** - No client-side JavaScript
- **Use Tailwind** - Utility-first CSS classes
- **Consistent naming** - PascalCase for components
## Component Organization
```
src/view/
├── components/
│ ├── ui/ # Generic UI components
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── input.tsx
│ │ └── avatar.tsx
│ ├── forms/ # Form-specific components
│ │ ├── login_form.tsx
│ │ └── post_form.tsx
│ ├── navigation/ # Nav components
│ │ ├── navbar.tsx
│ │ └── sidebar.tsx
│ └── icons/ # Icon components
│ ├── check_icon.tsx
│ └── user_icon.tsx
├── layouts/ # Layout components
│ ├── app_layout.tsx
│ └── auth_layout.tsx
└── pages/ # Page components
├── home/
└── about/
```
## SSR Considerations
Lockness uses **server-side rendering only**:
- No React hooks (useState, useEffect)
- No client-side state management
- No event handlers (onClick, onChange) - use forms
- Pure HTML output
- Excellent SEO
- Fast initial load
- Progressive enhancement
For interactivity:
- Use forms with server-side processing
- Use HTML attributes (autofocus, required)
- Use CSS for animations/transitions
- Add client-side JS separately if needed