another-novel-svelte v1.2.0
Novella Svelte
Heavily biased Notion-style WYSIWYG editor without the bells and whistles. I'm maintaining this for my blogging website called AcademiaScribes.
Based on Novel
Forked from Novel-Svelte
Made for DennisOluoch
Features
- š Rich Text Editor with Notion-style formatting
- š¼ļø Image uploads with drag & drop, paste, and file selection
- š¾ Auto-saving to local storage
- ā” Lightning fast and lightweight
- šØ Fully customizable with Tailwind CSS
- š± Mobile-friendly and responsive
Techs and Works
- Svelte - Frontend framework
- Tiptap - Headless editor framework
- Tailwind CSS - Styling
Installation
npm i another-novel-svelte
Basic Usage
<script>
import { Editor } from 'another-novel-svelte';
let saveStatus = 'Saved';
let editor;
// Image upload configuration
const imageProviderConfig = {
provider: 'vercel',
bucketName: 'your-bucket-name',
accessToken: 'your-access-token'
};
</script>
<main>
<Editor
bind:editor
{imageProviderConfig}
onUpdate={() => {
saveStatus = 'Unsaved';
}}
onDebouncedUpdate={() => {
saveStatus = 'Saving...';
// Saving code goes here
saveStatus = 'Saved';
}}
>
<div>
{saveStatus}
</div>
</Editor>
</main>
Image Upload Configuration
The editor supports image uploads to multiple providers: Vercel Blob, Supabase Storage, and Cloudinary. Instead of using environment variables, we now pass the configuration directly to the Editor component.
Configuration Setup
Create a configuration object in your Svelte component:
const imageProviderConfig = {
provider: 'vercel', // Options: 'vercel', 'supabase', 'cloudinary'
bucketName: 'your-bucket-name',
accessToken: 'your-access-token',
// Additional properties based on the provider:
// For Supabase:
// supabaseUrl: 'your-project-url'
// For Cloudinary:
// cloudinaryCloudName: 'your-cloud-name'
};
Then pass this configuration to the Editor component:
<Editor {imageProviderConfig} ...otherProps>
<!-- Editor content -->
</Editor>
Provider Setup Instructions
Vercel Blob
- Set up a Vercel project
- Get your Blob access token from the Vercel dashboard
- Configure the
imageProviderConfig
as shown above
Supabase Storage
- Create a Supabase project
- Create a storage bucket
- Set the storage bucket to public if you want the images to be publicly accessible
- Get your service role key from the project settings
- Configure the
imageProviderConfig
as shown above, including thesupabaseUrl
Cloudinary
- Create a Cloudinary account
- Create an upload preset (can be done in the Settings > Upload section)
- Get your cloud name and API credentials
- Configure the
imageProviderConfig
as shown above, including thecloudinaryCloudName
Image Upload Features
- š¤ Drag and drop image uploads
- š Paste images directly from clipboard
- š¼ļø Support for all standard image formats (jpg, png, gif, etc.)
- āļø Maximum file size: 20MB
- š Real-time upload status with toast notifications
- š Image resizing after upload
- š¾ Automatic image optimization
Security Considerations
ā ļø Important: The configuration passed to the Editor component will be visible in the client-side code. For production use, we strongly recommend implementing a server-side API endpoint to handle the actual upload operations.
Recommended Production Setup
- Create a server-side API endpoint (using SvelteKit or your preferred backend):
// routes/api/upload/+server.ts
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const file = await request.blob();
// Handle upload using server-side tokens
// Return the URL of the uploaded file
return json({ url: uploadedUrl });
}
- Use the server endpoint in production:
const uploadFile = async (file: File) => {
if (import.meta.env.DEV) {
// Use direct provider upload in development
return handleDirectUpload(file);
} else {
// Use server endpoint in production
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const { url } = await response.json();
return url;
}
};
Development
Install Node.js and npm (download here)
Clone the repository:
git clone https://github.com/DennisOluoch/novel-svelte.git
cd novel-svelte
- Install dependencies:
npm install
- Start the development server:
npm run dev
Visit http://localhost:5173/
to see the preview.
Props
Prop | Type | Default | Description |
---|---|---|---|
class | string | 'relative min-h-[500px] w-full max-w-screen-lg border-stone-200 bg-white p-12 px-8 sm:mb-[calc(20vh)] sm:rounded-lg sm:border sm:px-12 sm:shadow-lg' | Additional classes to add to the editor container |
defaultValue | JSONContent \| string | defaultEditorContent | The default value to use for the editor |
extensions | Extension[] | [] | Additional extensions to use for the editor |
editorProps | EditorProps | {} | Additional props to pass to the Tiptap editor |
onUpdate | (editor?: Editor) => void \| Promise<void> | noop | Callback function called on every editor update |
onDebouncedUpdate | (editor?: Editor) => void \| Promise<void> | noop | Callback function called after debounce duration |
debounceDuration | number | 750 | Duration to debounce the onDebouncedUpdate callback |
storageKey | string | 'novel__content' | Key to use for storing editor content in localStorage |
disableLocalStorage | boolean | false | Disable local storage read/save |
imageProviderConfig | UploadConfig \| undefined | undefined | Configuration for image upload provider |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License