Fully Managed React
Build your product, we'll take care of the rest.
Nokkio is the first full-stack React framework – from prototype to production including dev environments, build tooling, production hosting, databases, networking, data resolution, authentication, image uploads and resizing, SSR, and more.
You've configured webpack for the last time. Install the nokkio cli tool and use it to create a project, boot up your development environment, and deploy to production.
Web applications require real, persisted data. Nokkio provides a magical abstraction for your application's data. Define a schema using plain JavaScript and Nokkio takes care of managing your database, the backend API, and implementing your client-side networking code.
/** @type {import('@nokkio/schema').Config} */
module.exports = function ({ defineModel, types }) {
const List = defineModel('List', {
title: types.string(),
});
const Todo = defineModel('Todo', {
description: types.string(),
isComplete: types.bool(false),
});
List.hasMany(Todo);
return { List, Todo };
};
Once your schema is in place, immediately begin using data into your components without writing backend code, fumbling with API keys, or managing infrastructure.
import { usePageData } from '@nokkio/router';
import { List } from '@nokkio/magic';
export const getPageData = () => {
return List.find({ withCounts: ['todos'] });
};
export default function Index(): JSX.Element {
const lists = usePageData<typeof getPageData>();
return (
<ul>
{lists.map((list) => (
<li>
{list.title} ({list.todosCount} todos)
</li>
))}
</ul>
);
}
Mutations are supported out of the box using Nokkio's forms package. Nokkio keeps track of where your data is used, automatically refreshing just the components where that data is used.
import { usePageData } from '@nokkio/router';
import { List } from '@nokkio/magic';
import { Input, useForm } from '@nokkio/forms';
export const getPageData = () => {
return List.find({ withCounts: ['todos'] });
};
export default function Index(): JSX.Element {
const lists = usePageData<typeof getPageData>();
const { Form } = useForm(List);
return (
<div>
<Form>
<Input name="title" />
<button>Create list</button>
</Form>
<ul>
{lists.map((list) => (
<li>
{list.title} ({list.todosCount} todos)
</li>
))}
</ul>
</div>
);
}
Adding authentication to a Nokkio application is a one-liner and built atop Nokkio's native data abstraction.
/** @type {import('@nokkio/schema').Config} */
module.exports = function ({ defineModel, types }) {
const User = defineModel('User', {
username: types.string().unique(),
password: types.password(),
});
const List = defineModel('List', {
title: types.string(),
});
const Todo = defineModel('Todo', {
description: types.string(),
isComplete: types.bool(false),
});
User.hasMany(List);
List.hasMany(Todo);
User.actAsAuth();
return { List, Todo, User };
};
When Nokkio knows which model to use for authentication, it takes care of the rest–including built-in components that make login and registration forms a breeze.
import { useLoginForm } from '@nokkio/auth';
import { Input } from '@nokkio/forms';
export default function LoginPage(): JSX.Element {
const { Form } = useLoginForm();
return (
<Form>
<Input name="username" />
<Input name="password" />
<button>Login</button>
</Form>
);
}
When you're ready, deploy to a production environment custom-fit for Nokkio applications. Working with a team? Connect your repository to Nokkio’s Github application and get a new deploy for every push to your main branch, and preview deploys for every pull request.
Add an image field to your Nokkio model and get durable storage and automatic image resizing out of the box.
For the times when you do need backend logic, you can accept traffic to endpoints by defining a JavaScript function.
No need to manage complex local state. Nokkio knows when your data has changed and automatically refetches.
Prefer to write TypeScript? You'll have end-to-end type safety for your data. Want to use Tailwind? Support is baked in.
Subscribe to periodic updates via email from the Nokkio team.