1 January 2025
Streamline Your Development Process with a Custom GitHub Template and NPM

Ryan Knight

As developers, time spent on repetitive setup tasks can quickly add up. Setting up the same boilerplate for every project is tedious and inefficient. That’s why I created myself a custom GitHub template and an accompanying NPM package, create-rk-base, to speed up my workflow.
In this post, I'll walk you through why I created the rk-base template, focusing on the custom components like controlled forms, server action patterns, and theming. These components allow me to jumpstart any project without having to set up boilerplate code from scratch.
If you would like you can checkout my template here.
Why Create a GitHub Template Repository?
When I start a new project, I don’t need to set up everything from zero. I want the freedom to focus on what makes the project unique—features, business logic, and design. That’s where my GitHub template repository comes in.
The rk-base template is built on top of popular frameworks like Next.js and Tailwind. However, instead of just setting up these tools, I include my own custom components and patterns that I use in nearly every project:
- Server Action Pattern: Abstracts backend interactions like data fetching and database operations, allowing me to handle server-side logic in a reusable, consistent manner. Server actions are constantly changing with React 19 and Next js 15 so its nice to have one spot where I can expand or modify my pattern.
Here is a small example:
FormState.tsx
export type FormState<T = any> = {
message: string;
fields?: Record<string, string>;
issues?: string[];
isError: boolean;
data?: T;
};
ContactAction.tsx
'use server';
export async function contactAction(
prevState: FormState,
data: FormData,
): Promise<FormState> {
const formData = Object.fromEntries(data);
const parsed = contactSchema.safeParse(formData);
if (!parsed.success) {
return handleInvalidForm({ formData, parsed });
}
try {
const client = await systemApi();
const res = await client.supportContactusPost({
contactUs: {
email: parsed.data.email,
message: JSON.stringify(parsed.data),
name: parsed.data.firstName,
},
});
} catch (error) {
if (isAxiosError(error)) {
console.error(error.message);
return { message: error.message, isError: true };
}
}
return { message: 'success', isError: false };
}
- Controlled Forms: Reusable, flexible components that handle form state and validation out of the box. I love React hook form and Shadcn. Together I always use controlled components and I dont have to recreate them now. I can just wrap a FormProvider around my form and drop in any controlled input I like. Here is an example:
RevokeInvitation.tsx
export const RevokeInvitation = ({ invitationId }: Props) => {
const { runAction, isPending, form, formRef } = useActionLifeCycle({
action: revokeInvitationAction,
schema: revokeInvitationSchema,
defaultFormValues: {
companyId: '123',
invitationId,
},
});
return (
<Form {...form}>
<form ref={formRef} className='grid gap-8'>
{Object.keys(revokeInvitationSchema.shape).map((input) => (
<FormInput key={input} type='hidden' name={input} />
))}
<DropdownMenuItem
className='cursor-pointer'
onClick={form.handleSubmit(runAction)}
>
{isPending ? (
<ReloadIcon className='mr-2 h-4 w-4 animate-spin' />
) : (
<Trash2 className='mr-2 h-4 w-4' />
)}
<span>{isPending ? 'Revoking...' : 'Revoke'}</span>
</DropdownMenuItem>
</form>
</Form>
);
};
FormInput.tsx
'use client';
interface Props extends InputHTMLAttributes<HTMLInputElement> {
label?: string;
name: string;
description?: string;
}
export const FormInput = ({
label,
name,
description,
className,
...rest
}: Props) => {
const { control } = useFormContext();
return (
<FormField
control={control}
name={name}
defaultValue={''}
render={({ field }) => (
<FormItem className={cn(rest.type, className)}>
<FormLabel>{label}</FormLabel>
<FormControl>
<Input {...field} {...rest} />
</FormControl>
<FormDescription>{description}</FormDescription>
<FormMessage />
</FormItem>
)}
/>
);
};
- Theme Provider: A built-in mechanism for easily switching between light and dark modes across my apps.
Here is an example:
"use client";
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes/dist/types";
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
By using a template, I eliminate the need to manually configure these elements every time I start a new project.
The Process of Creating the GitHub Template Repository
Creating a GitHub template repository is simple, but it requires some thought about the components you want to reuse. Here's how I created mine:
Set up your base project: Start with a basic Next.js or another framework of your choice. This is where tools like create-next-app come in handy.
npx create-next-app@latest
cd yourProject
Add reusable components and patterns: After setting up the base project, I added:
- Custom UI components (e.g., buttons, modals, forms)
- A theme provider for light/dark mode
- A server action pattern to handle server-side logic efficiently
Push to GitHub: Once everything is set up, I pushed the project to a new GitHub repository and turned it into a template by checking the “Template repository” box in the settings.
git init
git remote add origin https://your-repo-url
git add .
git commit -m "Initial commit"
git push -u origin main
Now, anyone can use this template to start their own project by simply cloning the repo.
Automating with the create-rk-stack NPM Package
The next step in my workflow is automating the setup process. Rather than manually cloning the template and configuring the project, I created an NPM package that automates it with a simple command: npm create-rk-stack.
Here’s how I built it:
- Init a new package.json: create a directory with a package.json file in it (you can simply run npm init to start this process).
{
"name": "create-rk-base",
"version": "1.0.1",
"description": "This is my base stack based off of Next js, tailwind, and shadcn.",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Ryan Knight",
"license": "ISC",
"bin": {
"create-rk-base": "index.js"
},
"dependencies": {
"inquirer": "^12.3.0"
}
}
- Create a Node.js CLI: I wrote a small script index.js, using Node.js to automate the project setup process. The script uses inquirer to ask for the project name and git to clone the repository.
#!/usr/bin/env node
import { exec } from 'child_process';
import path from 'path';
import fs from 'fs/promises';
import inquirer from 'inquirer';
const GITHUB_URL = 'https://github.com/ryanknight09/rk-stack';
inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: 'Enter the project name:',
validate(input) {
if (!input) {
return 'Project name cannot be empty';
}
return true;
}
}
]).then(async (answers) => {
const projectName = answers.projectName;
const projectDir = path.join(process.cwd(), projectName);
// Clone the template repo
exec(`git clone ${GITHUB_URL} ${projectDir}`, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
return;
}
console.log(stdout);
console.error(stderr);
});
});
- Publish to NPM: After writing the script, I packaged it into an NPM package and published it to the NPM registry, so anyone can use it.
npm login
npm publish
- Usage: Now, you can simply run the command npm create-rk-base to initialize a new project based on my template. The process is automated, saving you time and ensuring that your project starts with the right structure.
npm install -g create-rk-base
npm create-rk-base
Why Have an NPM Command Instead of Just a Template Repository?
While a GitHub template repository is great for creating a base project, using an NPM command like npm create-rk-base takes it to the next level by automating the entire process. Here’s why it’s beneficial to have an NPM command in addition to the template repository:
Automation and Convenience: Using an NPM package simplifies the process of creating a new project. With a simple command "npm create-rk-base", you can automatically clone the template, rename the project, and install dependencies—all without manually running several commands in the terminal. This eliminates potential error and saves time on setup
Consistency: By running the NPM command, you ensure that every new project follows the same structure without skipping steps. The automation script handles project naming, dependency installation, and the setup process consistently every time you use it. This prevents any variations that might arise if you were to clone and set up the repo manually.
No Need to Manually Clone and Configure: Without the NPM command, you'd have to:
- Clone the GitHub template manually.
- Rename the project.
- Install dependencies.
- The NPM package automates all of this in one step. It also handles potential pitfalls like overwriting an existing directory and ensuring that the project name is reflected in the package.json file.
Faster Setup: An NPM package streamlines your workflow. Instead of navigating GitHub, cloning repositories, and performing several manual setup steps, you can get your project up and running in minutes with a single command. This gives you more time to focus on building features rather than dealing with configuration.
Easily Shareable: By publishing the create-rk-base command to NPM, anyone can use it without needing to manually configure anything. This is especially helpful in teams or open-source projects where multiple people need to start new projects from the same template.
You can try it for yourself:
npm install -g create-rk-base
npm create-rk-base
Conclusion
By combining a custom GitHub template with an NPM package, I’ve streamlined my development process and reduced the time spent on setup tasks. The rk-base template, which includes reusable components like controlled forms, server actions, and a theme provider, ensures that I can start building immediately on any project.
If you find yourself repeatedly setting up the same things across projects, I highly recommend creating your own template repository and NPM package to automate the process. With just a few commands, you’ll be able to focus on what matters: delivering value through code.