Features

Generate UIs for any framework

SpecUI breaks down the barriers of language-specific limitations, offering you the freedom to generate code in the language of your choice. Use or create generators for your favorite frameworks.

Breeze through migrations and upgrades

Your needs and dependencies change constantly. It's easy to fall behind and can even bring your app to a sudden hault. SpecUI enables you to effortlessly adapt your existing codebase to new standards and upgrades, minimizing manual effort and reducing the scope for errors.

Upgrading from Class Components to Function Components

spec.yml
pages:
  index:
    elements:
      - tag: h1
        text: Hello World
home.jsx
import { Component } from "react";

class HomePage extends Component {
  render() {
    return <h1>Hello World</h1>;
  }
}
home.tsx
import { FC } from "react";

const HomePage: FC = () =>
  <h1>Hello World</h1>;

Migrating from Pages to App Router

spec.yml
models:
  task:
    attributes:
      id:
        key: primary
        type: number
      description:
        required: true
        type: string
      isCompleted:
        default: false
        type: boolean
pages:
  index:
    elements:
      - tag: ul
        elements:
          model: tasks
          name: task
          key: $task.id
          tag: li
          class: flex justify-between min-w-96
          elements:
            - tag: label
              text: $task.description
              for: isCompleted
            - tag: component
              component: checkbox
              defaultChecked: $task.isCompleted
              name: isCompleted
pages/index.tsx
import type { InferGetStaticPropsType, GetStaticProps } from 'next'
import { type Task, db } from '@/lib/db';

export default function Home({
  tasks,
}: InferGetStaticPropsType<typeof getStaticProps>) {
  return (
    <ul className="flex flex-col gap-2">
      {tasks.map((task) => (
        <li key={task.id}>
          <label htmlFor="isCompleted">{task.description}</label>
          <Checkbox defaultChecked={task.isCompleted} name="isCompleted" />
        </li>
      ))}
    </ul>
  );
}

export const getStaticProps = (async (context) => {
  const tasks = await db
    .selectFrom('tasks')
    .selectAll()
    .execute();
  return { props: { tasks } };
}) satisfies GetStaticProps<{
  tasks: Task[]
}>
app/page.tsx
import type { InferGetStaticPropsType, GetStaticProps } from 'next'
import { type Task, db } from '@/lib/db';

export default async function Home() {
  const tasks = await db
    .selectFrom('tasks')
    .selectAll()
    .execute();

  return (
    <ul className="flex flex-col gap-2">
      {tasks.map((task) => (
        <li key={task.id}>
          <label htmlFor="isCompleted">{task.description}</label>
          <Checkbox defaultChecked={task.isCompleted} name="isCompleted" />
        </li>
      ))}
    </ul>
  );
}

Keep your codebase consistent

Maintaining a consistent codebase is hard. But it is the key to efficiency and scalability. Naming conventions, design patterns and coding standards are a big part of this. SpecUI ensures uniformity across your entire project.

spec.yml
models:
  post:
    attributes:
      title:
        required: true
        type: string
        unique: true
      content:
        required: true
        type: string
  user:
    attributes:
      name:
        type: string
PostsTable.ts
import { ColumnType, Generated, sql } from 'kysely'

import { db } from '@/lib/db'

export interface PostsTable {
  id: Generated<number>
  title: string
  content: string
  createdAt: ColumnType<Date, string | undefined, never>
}

export const createPostsTable = async () => {
  await db.schema
    .createTable('posts')
    .ifNotExists()
    .addColumn('id', 'serial', (cb) => cb.primaryKey())
    .addColumn('title', 'varchar(255)', (cb) => cb.notNull().unique())
    .addColumn('content', 'varchar(255)', (cb) => cb.notNull())
    .addColumn('createdAt', sql`timestamp with time zone`, (cb) =>
      cb.defaultTo(sql`current_timestamp`)
    )
    .execute()
}
UsersTable.ts
import { ColumnType, Generated, sql } from 'kysely'

import { db } from '@/lib/db'

export interface UsersTable {
  id: Generated<number>
  name?: string
  createdAt: ColumnType<Date, string | undefined, never>
}

export const createUsersTable = async () => {
  await db.schema
    .createTable('users')
    .ifNotExists()
    .addColumn('id', 'serial', (cb) => cb.primaryKey())
    .addColumn('name', 'varchar(255)')
    .addColumn('createdAt', sql`timestamp with time zone`, (cb) =>
      cb.defaultTo(sql`current_timestamp`)
    )
    .execute()
}

Generate code, continuously

Many code generators enable to produce your code once.

Create a simple model

spec.yml
models:
  user:
    attributes:
      name:
        type: string
post.go (generated)
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

Add attributes to your model

spec.yml
models:
  user:
    attributes:
      name:
        type: string
post.go (generated)
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

Rename your model

spec.yml
models:
  user:
    attributes:
      name:
        type: string
post.go (generated)
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

Change the language of your model

spec.yml
models:
  user:
    attributes:
      name:
        type: string
post.go (generated)
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

Use alongside your handwritten code

Effortlessly blend automated and manual code. Designed for synergy, SpecUI allows you to generate essential code structures automatically while providing the flexibility to write your custom code alongside.

Choose whether to generate your models, handlers, both, or neither

spec.yml
models:
  post:
    attributes:
      title:
        type: string
      content:
        type: string
post.go (generated)
package models

import "time"

type Post struct {
    ID        int       `json:"id"`
    Title     string    `json:"title"`
    Content   string    `json:"content"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}
post_handlers.go (handwritten)
package handlers

import (
    "net/http"
    "time"

    "github.com/gin-gonic/gin"
    "gorm.io/gorm"
    "my-app/models"
)

func CreatePost(db *gorm.DB) gin.HandlerFunc {
  return func(c *gin.Context) {
      var newPost models.Post
      if err := c.ShouldBindJSON(&newPost); err != nil {
          c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
          return
      }

      // write some other code

      db.Create(&newPost)
      c.JSON(http.StatusCreated, newPost)
  }
}

Spec how you want

The power to define your specifications lies in your hands. Embrace the freedom to use industry-standard specifications for familiarity and best practices, or blaze your own trail with custom specifications tailored to your unique requirements.

Use a standard like Swagger or roll with your own.

spec.yml
paths:
  /pet:
    post:
      operationId: addPet
      requestBody:
        description: Create a new pet in the store
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Pet'
        required: true
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'
        '405':
          description: Invalid input
spec.yml
calls:
  addPet:
    description: Create a new pet in the store
    request:
      $ref: '#/models/pet' 
    response:
      $ref: '#/models/pet'

Leave anytime

If you ever want to stop using SpecUI, just remove the `.specui` directory and keep shipping. We will miss you and are honored to be a part of your journey.

spec.yml
models:
  post:
    attributes:
      title:
        type: string
      content:
        type: string
post.go
package models

import "time"

type Post struct {
    ID        int       `json:"id"`
    Title     string    `json:"title"`
    Content   string    `json:"content"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}
Terminal
$ rm -rf .specui

Want to learn more?

Try It OnlineRead the Docs